Skip to content

Commit cf93169

Browse files
committed
Make APK file format is available with R2 lib
1 parent 62df4b0 commit cf93169

File tree

1 file changed

+42
-51
lines changed

1 file changed

+42
-51
lines changed

quark/core/r2apkinfo.py

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

26-
R2Cache = namedtuple("r2_cache", "address dexindex is_imported")
26+
R2Cache = namedtuple("r2_cache", "address is_imported")
2727

2828
PRIMITIVE_TYPE_MAPPING = {
2929
"void": "V",
@@ -37,7 +37,6 @@
3737
"double": "D",
3838
}
3939

40-
# R2_ESCAPE_CHAR_LIST = ["<", ">", "$"]
4140
R2_ESCAPE_CHAR_LIST = ["$"]
4241

4342

@@ -78,15 +77,19 @@ def __init__(
7877
self._number_of_dex = len(self._dex_list)
7978

8079
@functools.lru_cache
81-
def _get_r2(self, index):
80+
def _get_r2(self):
8281
"""
8382
Return a R2 object that opens the specified Dex file.
8483
8584
:param index: an index indicating which Dex file should the returned
8685
object open
8786
:return: a R2 object opening the specified Dex file
8887
"""
89-
r2 = r2pipe.open(self._dex_list[index])
88+
if self.ret_type == "DEX":
89+
r2 = r2pipe.open(self._dex_list[0])
90+
elif self.ret_type == "APK":
91+
r2 = r2pipe.open(f"apk://{self.apk_filepath}")
92+
9093
r2.cmd("aa")
9194
return r2
9295

@@ -125,7 +128,6 @@ def _convert_type_to_type_signature(self, raw_type: str):
125128
raw_type = raw_type.replace("_", "$")
126129
return "L" + raw_type + ";"
127130

128-
# return "Ljava/lang/" + raw_type + ";"
129131
return raw_type + ";"
130132

131133
@staticmethod
@@ -142,7 +144,7 @@ def _escape_str_in_r2_manner(raw_str: str):
142144
raw_str = raw_str.replace(c, "_")
143145
return raw_str
144146

145-
def _parse_method_from_isj_obj(self, json_obj, dexindex):
147+
def _parse_method_from_isj_obj(self, json_obj):
146148
"""
147149
Parse a JSON object provided by the R2 command `isj` or `is.j` into
148150
an instance of MethodObject.
@@ -188,13 +190,13 @@ def _parse_method_from_isj_obj(self, json_obj, dexindex):
188190
class_name=class_name,
189191
name=method_name,
190192
descriptor=descriptor,
191-
cache=R2Cache(json_obj["vaddr"], dexindex, is_imported),
193+
cache=R2Cache(json_obj["vaddr"], is_imported),
192194
)
193195

194196
return method
195197

196198
@functools.lru_cache
197-
def _get_methods_classified(self, dexindex):
199+
def _get_methods_classified(self):
198200
"""
199201
Parse all methods in the specified Dex and convert them into a
200202
dictionary. The dictionary takes their belonging classes as the keys.
@@ -205,12 +207,12 @@ def _get_methods_classified(self, dexindex):
205207
:return: a dictionary taking a class name as the key and a list of
206208
MethodObject as the corresponding value.
207209
"""
208-
r2 = self._get_r2(dexindex)
210+
r2 = self._get_r2()
209211

210212
method_json_list = r2.cmdj("isj")
211213
method_dict = defaultdict(list)
212214
for json_obj in method_json_list:
213-
method = self._parse_method_from_isj_obj(json_obj, dexindex)
215+
method = self._parse_method_from_isj_obj(json_obj)
214216

215217
if method:
216218
method_dict[method.class_name].append(method)
@@ -306,7 +308,7 @@ def all_methods(self) -> Set[MethodObject]:
306308
"""
307309
method_set = set()
308310
for dex_index in range(self._number_of_dex):
309-
for method_list in self._get_methods_classified(dex_index).values():
311+
for method_list in self._get_methods_classified().values():
310312
method_set.update(method_list)
311313

312314
return method_set
@@ -354,13 +356,13 @@ def method_filter(method):
354356

355357
if class_name != ".*":
356358
for dex_index in dex_list:
357-
method_dict = self._get_methods_classified(dex_index)
359+
method_dict = self._get_methods_classified()
358360
filtered_methods += list(
359361
filter(method_filter, method_dict[class_name])
360362
)
361363
else:
362364
for dex_index in dex_list:
363-
method_dict = self._get_methods_classified(dex_index)
365+
method_dict = self._get_methods_classified()
364366
for key_name in method_dict:
365367
filtered_methods += list(
366368
filter(method_filter, method_dict[key_name])
@@ -380,10 +382,9 @@ def upperfunc(self, method_object: MethodObject) -> Set[MethodObject]:
380382
"""
381383
cache = method_object.cache
382384

383-
r2 = self._get_r2(cache.dexindex)
384-
385-
xrefs = r2.cmdj(f"axtj @ {cache.address}")
385+
r2 = self._get_r2()
386386

387+
xrefs = r2.cmdj(f"axlj @ {cache.address}")
387388
upperfunc_set = set()
388389
for xref in xrefs:
389390
if xref["type"] != "CALL":
@@ -421,8 +422,7 @@ def lowerfunc(
421422
"""
422423
cache = method_object.cache
423424

424-
r2 = self._get_r2(cache.dexindex)
425-
425+
r2 = self._get_r2()
426426
instruct_flow = r2.cmdj(f"pdfj @ {cache.address}")["ops"]
427427

428428
lowerfunc_list = []
@@ -462,7 +462,7 @@ def get_method_bytecode(
462462
cache = method_object.cache
463463
if not cache.is_imported:
464464

465-
r2 = self._get_r2(cache.dexindex)
465+
r2 = self._get_r2()
466466

467467
instruct_flow = r2.cmdj(f"pdfj @ {cache.address}")["ops"]
468468
if instruct_flow:
@@ -480,13 +480,12 @@ def get_strings(self) -> Set[str]:
480480
:return: a set of strings
481481
"""
482482
strings = set()
483-
for dex_index in range(self._number_of_dex):
484-
r2 = self._get_r2(dex_index)
483+
r2 = self._get_r2()
485484

486-
string_detail_list = r2.cmdj("izzj")
487-
strings.update(
488-
[string_detail["string"] for string_detail in string_detail_list]
489-
)
485+
string_detail_list = r2.cmdj("izzj")
486+
strings.update(
487+
[string_detail["string"] for string_detail in string_detail_list]
488+
)
490489

491490
return strings
492491

@@ -536,7 +535,7 @@ def convert_bytecode_to_list(bytecode):
536535
if cache.is_imported:
537536
return {}
538537

539-
r2 = self._get_r2(cache.dexindex)
538+
r2 = self._get_r2()
540539

541540
instruction_flow = r2.cmdj(f"pdfj @ {cache.address}")["ops"]
542541

@@ -587,18 +586,16 @@ def superclass_relationships(self) -> Dict[str, Set[str]]:
587586
"""
588587
hierarchy_dict = defaultdict(set)
589588

590-
for dex_index in range(self._number_of_dex):
591-
592-
r2 = self._get_r2(dex_index)
589+
r2 = self._get_r2()
593590

594-
class_info_list = r2.cmdj("icj")
595-
for class_info in class_info_list:
596-
class_name = class_info["classname"]
597-
class_name = self._convert_type_to_type_signature(class_name)
598-
super_classes = class_info["super"]
591+
class_info_list = r2.cmdj("icj")
592+
for class_info in class_info_list:
593+
class_name = class_info["classname"]
594+
class_name = self._convert_type_to_type_signature(class_name)
595+
super_classes = class_info["super"]
599596

600-
for super_class in super_classes:
601-
hierarchy_dict[class_name].add(super_class)
597+
for super_class in super_classes:
598+
hierarchy_dict[class_name].add(super_class)
602599

603600
return hierarchy_dict
604601

@@ -616,16 +613,14 @@ def subclass_relationships(self) -> Dict[str, Set[str]]:
616613
"""
617614
hierarchy_dict = defaultdict(set)
618615

619-
for dex_index in range(self._number_of_dex):
616+
r2 = self._get_r2()
620617

621-
r2 = self._get_r2(dex_index)
618+
class_info_list = r2.cmdj("icj")
619+
for class_info in class_info_list:
620+
class_name = class_info["classname"]
621+
super_class = class_info["super"]
622622

623-
class_info_list = r2.cmdj("icj")
624-
for class_info in class_info_list:
625-
class_name = class_info["classname"]
626-
super_class = class_info["super"]
627-
628-
hierarchy_dict[super_class].add(class_name)
623+
hierarchy_dict[super_class].add(class_name)
629624

630625
return hierarchy_dict
631626

@@ -636,14 +631,12 @@ def _get_method_by_address(self, address: int) -> MethodObject:
636631
:param address: an address used to find the corresponding method
637632
:return: the MethodObject of the method in the given address
638633
"""
639-
dexindex = 0
640-
641-
r2 = self._get_r2(dexindex)
634+
r2 = self._get_r2()
642635
json_data = r2.cmdj(f"is.j @ {address}")
643636
json_data = json_data.get("symbols")
644637

645638
if json_data:
646-
return self._parse_method_from_isj_obj(json_data, dexindex)
639+
return self._parse_method_from_isj_obj(json_data)
647640
else:
648641
return None
649642

@@ -654,9 +647,7 @@ def _get_string_by_address(self, address: str) -> str:
654647
:param address: an address used to find the corresponding method
655648
:return: the content in the given address
656649
"""
657-
dexindex = 0
658-
659-
r2 = self._get_r2(dexindex)
650+
r2 = self._get_r2()
660651
content = r2.cmd(f"pr @ {int(address, 16)}")
661652
return content
662653

0 commit comments

Comments
 (0)