2323 remove_dup_list ,
2424)
2525
26- RizinCache = namedtuple ("rizin_cache" , "address dexindex is_imported" )
26+ RizinCache = namedtuple ("rizin_cache" , "address is_imported" )
2727
2828PRIMITIVE_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