Skip to content

Commit 7eb70fd

Browse files
committed
Make APK file format is available with Rizin lib and remove unused code
1 parent 136d0b6 commit 7eb70fd

File tree

1 file changed

+48
-98
lines changed

1 file changed

+48
-98
lines changed

quark/core/rzapkinfo.py

Lines changed: 48 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
remove_dup_list,
2424
)
2525

26-
RizinCache = namedtuple("rizin_cache", "address dexindex is_imported")
26+
RizinCache = namedtuple("rizin_cache", "address is_imported")
2727

2828
PRIMITIVE_TYPE_MAPPING = {
2929
"void": "V",
@@ -60,32 +60,21 @@ def __init__(
6060

6161
self._manifest = os.path.join(self._tmp_dir, "AndroidManifest.xml")
6262

63-
dex_files = [
64-
file
65-
for file in apk.namelist()
66-
if file.startswith("classes") and file.endswith(".dex")
67-
]
68-
69-
for dex in dex_files:
70-
apk.extract(dex, path=self._tmp_dir)
71-
72-
self._dex_list = [os.path.join(self._tmp_dir, dex) for dex in dex_files]
73-
7463
else:
7564
raise ValueError("Unsupported File type.")
7665

77-
self._number_of_dex = len(self._dex_list)
78-
79-
@functools.lru_cache
80-
def _get_rz(self, index):
66+
@functools.cached_property
67+
def _rz(self):
8168
"""
82-
Return a Rizin object that opens the specified Dex file.
69+
Return a Rizin object that opens the specified Dex file or APK file.
8370
84-
:param index: an index indicating which Dex file should the returned
85-
object open
8671
:return: a Rizin object opening the specified Dex file
8772
"""
88-
rz = rzpipe.open(self._dex_list[index])
73+
if self.ret_type == "DEX":
74+
rz = rzpipe.open(f"{self.apk_filepath}")
75+
elif self.ret_type == "APK":
76+
rz = rzpipe.open(f"apk://{self.apk_filepath}")
77+
8978
rz.cmd("aa")
9079
return rz
9180

@@ -140,15 +129,13 @@ def _escape_str_in_rizin_manner(raw_str: str):
140129
raw_str = raw_str.replace(c, "_")
141130
return raw_str
142131

143-
def _parse_method_from_isj_obj(self, json_obj, dexindex):
132+
def _parse_method_from_isj_obj(self, json_obj):
144133
"""
145134
Parse a JSON object provided by the Rizin command `isj` or `is.j` into
146135
an instance of MethodObject.
147136
148137
:param json_obj: a JSON object provided by the Rizin command `isj` or
149138
`is.j`
150-
:param dexindex: an index indicating from which Dex file the JSON
151-
object is generated
152139
:return: an instance of MethodObject
153140
"""
154141
if json_obj.get("type") not in ["FUNC", "METH"]:
@@ -220,7 +207,7 @@ def _parse_method_from_isj_obj(self, json_obj, dexindex):
220207
class_name="",
221208
name="clone",
222209
descriptor="()Ljava/lang/Object;",
223-
cache=RizinCache(json_obj["vaddr"], dexindex, is_imported),
210+
cache=RizinCache(json_obj["vaddr"], is_imported),
224211
)
225212
return method
226213

@@ -250,28 +237,23 @@ def _parse_method_from_isj_obj(self, json_obj, dexindex):
250237
class_name=class_name,
251238
name=method_name,
252239
descriptor=descriptor,
253-
cache=RizinCache(json_obj["vaddr"], dexindex, is_imported),
240+
cache=RizinCache(json_obj["vaddr"], is_imported),
254241
)
255242

256243
return method
257244

258245
@functools.lru_cache
259-
def _get_methods_classified(
260-
self, dex_index: int
261-
) -> Dict[str, List[MethodObject]]:
246+
def _get_methods_classified(self) -> Dict[str, List[MethodObject]]:
262247
"""
263248
Use command isj to get all the methods and categorize them into
264249
a dictionary.
265250
266-
:param dex_index: an index to the Dex file that need to be parsed.
267251
:return: a dict that holds methods categorized by their class name
268252
"""
269-
rz = self._get_rz(dex_index)
270-
271-
method_json_list = rz.cmdj("isj")
253+
method_json_list = self._rz.cmdj("isj")
272254
method_dict = defaultdict(list)
273255
for json_obj in method_json_list:
274-
method = self._parse_method_from_isj_obj(json_obj, dex_index)
256+
method = self._parse_method_from_isj_obj(json_obj)
275257
if method:
276258
method_dict[method.class_name].append(method)
277259

@@ -377,9 +359,8 @@ def all_methods(self) -> Set[MethodObject]:
377359
:return: a set of MethodObjects
378360
"""
379361
method_set = set()
380-
for dex_index in range(self._number_of_dex):
381-
for method_list in self._get_methods_classified(dex_index).values():
382-
method_set.update(method_list)
362+
for method_list in self._get_methods_classified().values():
363+
method_set.update(method_list)
383364

384365
return method_set
385366

@@ -421,22 +402,19 @@ def method_filter(method):
421402
descriptor, method.descriptor
422403
)
423404

424-
dex_list = range(self._number_of_dex)
425405
filtered_methods = list()
426406

427407
if class_name != ".*":
428-
for dex_index in dex_list:
429-
method_dict = self._get_methods_classified(dex_index)
408+
method_dict = self._get_methods_classified()
409+
filtered_methods += list(
410+
filter(method_filter, method_dict[class_name])
411+
)
412+
else:
413+
method_dict = self._get_methods_classified()
414+
for key_name in method_dict:
430415
filtered_methods += list(
431-
filter(method_filter, method_dict[class_name])
416+
filter(method_filter, method_dict[key_name])
432417
)
433-
else:
434-
for dex_index in dex_list:
435-
method_dict = self._get_methods_classified(dex_index)
436-
for key_name in method_dict:
437-
filtered_methods += list(
438-
filter(method_filter, method_dict[key_name])
439-
)
440418

441419
return filtered_methods
442420

@@ -452,10 +430,7 @@ def upperfunc(self, method_object: MethodObject) -> Set[MethodObject]:
452430
"""
453431
cache = method_object.cache
454432

455-
r2 = self._get_rz(cache.dexindex)
456-
457-
xrefs = r2.cmdj(f"axtj @ {cache.address}")
458-
433+
xrefs = self._rz.cmdj(f"axtj @ {cache.address}")
459434
upperfunc_set = set()
460435
for xref in xrefs:
461436
if xref["type"] != "CALL":
@@ -493,9 +468,7 @@ def lowerfunc(
493468
"""
494469
cache = method_object.cache
495470

496-
rz = self._get_rz(cache.dexindex)
497-
498-
instruct_flow = rz.cmdj(f"pdfj @ {cache.address}")["ops"]
471+
instruct_flow = self._rz.cmdj(f"pdfj @ {cache.address}")["ops"]
499472

500473
lowerfunc_list = []
501474
for ins in instruct_flow:
@@ -532,13 +505,9 @@ def get_method_bytecode(
532505
:yield: a generator of BytecodeObjects
533506
"""
534507
cache = method_object.cache
535-
536508
if not cache.is_imported:
537509

538-
rz = self._get_rz(cache.dexindex)
539-
540-
instruct_flow = rz.cmdj(f"pdfj @ {cache.address}")["ops"]
541-
510+
instruct_flow = self._rz.cmdj(f"pdfj @ {cache.address}")["ops"]
542511
if instruct_flow:
543512
for ins in instruct_flow:
544513
if "disasm" not in ins:
@@ -554,13 +523,10 @@ def get_strings(self) -> Set[str]:
554523
:return: a set of strings
555524
"""
556525
strings = set()
557-
for dex_index in range(self._number_of_dex):
558-
rz = self._get_rz(dex_index)
559-
560-
string_detail_list = rz.cmdj("izzj")
561-
strings.update(
562-
[string_detail["string"] for string_detail in string_detail_list]
563-
)
526+
string_detail_list = self._rz.cmdj("izzj")
527+
strings.update(
528+
[string_detail["string"] for string_detail in string_detail_list]
529+
)
564530

565531
return strings
566532

@@ -610,9 +576,7 @@ def convert_bytecode_to_list(bytecode):
610576
if cache.is_imported:
611577
return {}
612578

613-
rz = self._get_rz(cache.dexindex)
614-
615-
instruction_flow = rz.cmdj(f"pdfj @ {cache.address}")["ops"]
579+
instruction_flow = self._rz.cmdj(f"pdfj @ {cache.address}")["ops"]
616580

617581
if instruction_flow:
618582
for ins in instruction_flow:
@@ -661,18 +625,14 @@ def superclass_relationships(self) -> Dict[str, Set[str]]:
661625
"""
662626
hierarchy_dict = defaultdict(set)
663627

664-
for dex_index in range(self._number_of_dex):
665-
666-
rz = self._get_rz(dex_index)
628+
class_info_list = self._rz.cmdj("icj")
629+
for class_info in class_info_list:
630+
class_name = class_info["classname"]
631+
class_name = self._convert_type_to_type_signature(class_name)
632+
super_class = class_info["super"]
633+
super_class = self._convert_type_to_type_signature(super_class)
667634

668-
class_info_list = rz.cmdj("icj")
669-
for class_info in class_info_list:
670-
class_name = class_info["classname"]
671-
class_name = self._convert_type_to_type_signature(class_name)
672-
super_class = class_info["super"]
673-
super_class = self._convert_type_to_type_signature(super_class)
674-
675-
hierarchy_dict[class_name].add(super_class)
635+
hierarchy_dict[class_name].add(super_class)
676636

677637
return hierarchy_dict
678638

@@ -690,16 +650,12 @@ def subclass_relationships(self) -> Dict[str, Set[str]]:
690650
"""
691651
hierarchy_dict = defaultdict(set)
692652

693-
for dex_index in range(self._number_of_dex):
694-
695-
rz = self._get_rz(dex_index)
653+
class_info_list = self._rz.cmdj("icj")
654+
for class_info in class_info_list:
655+
class_name = class_info["classname"]
656+
super_class = class_info["super"]
696657

697-
class_info_list = rz.cmdj("icj")
698-
for class_info in class_info_list:
699-
class_name = class_info["classname"]
700-
super_class = class_info["super"]
701-
702-
hierarchy_dict[super_class].add(class_name)
658+
hierarchy_dict[super_class].add(class_name)
703659

704660
return hierarchy_dict
705661

@@ -710,13 +666,10 @@ def _get_method_by_address(self, address: int) -> MethodObject:
710666
:param address: an address used to find the corresponding method
711667
:return: the MethodObject of the method in the given address
712668
"""
713-
dexindex = 0
714-
715-
rz = self._get_rz(dexindex)
716-
json_array = rz.cmdj(f"is.j @ {address}")
669+
json_array = self._rz.cmdj(f"is.j @ {address}")
717670

718671
if json_array:
719-
return self._parse_method_from_isj_obj(json_array[0], dexindex)
672+
return self._parse_method_from_isj_obj(json_array[0])
720673
else:
721674
return None
722675

@@ -727,10 +680,7 @@ def _get_string_by_address(self, address: str) -> str:
727680
:param address: an address used to find the corresponding method
728681
:return: the content in the given address
729682
"""
730-
dexindex = 0
731-
732-
rz = self._get_rz(dexindex)
733-
content = rz.cmd(f"pr @ {int(address, 16)}")
683+
content = self._rz.cmd(f"pr @ {int(address, 16)}")
734684
return content
735685

736686
@staticmethod

0 commit comments

Comments
 (0)