def __init__(self, apk_path): sdk_path = os.path.join(os.path.expanduser("~"), "Android/Sdk/platforms/") if not os.path.exists(sdk_path): print( "cannot run test_apk_loading since there is no Android SDK folder" ) sys.exit(1) lifter = Lifter(path_apk, input_format="apk", android_sdk=sdk_path) self.p = turi.Project(apk_path, input_format='apk', android_sdk=sdk_path, lifter=lifter) self.apk_path = apk_path _, _, self.dx = AnalyzeAPK(apk_path) self.call_graph = self.dx.get_call_graph() self.avg_math_ops = self.get_avg_math_ops() self.sweet_spots = [] self.sweet_objs = [] self.reran_proc = None self.has_dominant = False self.entropies = {}
def _simple1_tests(ir_format): jar = os.path.join(test_samples_folder, "simple1.jar") lifter = Lifter(jar, ir_format=ir_format) classes = lifter.classes print(classes.keys()) assert "simple1.Class1" in classes.keys() assert "simple1.Class2" in classes.keys() test_str = ["r0", "public", "specialinvoke"] assert all([t in str(classes['simple1.Class1']) for t in test_str]) assert all([t in str(classes['simple1.Class2']) for t in test_str])
def setup(self): should_pickle = False should_unpickle = False if self.pickled is not None: pickled_path = os.path.abspath(self.pickled) if os.path.exists(pickled_path): should_unpickle = True else: should_pickle = True if should_unpickle: with open(pickled_path, 'rb') as fp: self._classes = pickle.load(fp) else: if not self._lifter: log.info('Lifting app') if self.android_sdk is not None and self.input_format is not None: self._lifter = Lifter(self.app_path, input_format=self.input_format, android_sdk=self.android_sdk) else: self._lifter = Lifter(self.app_path) self._classes = self._lifter.classes if should_pickle: with open(pickled_path, 'wb') as fp: pickle.dump(self._classes, fp, protocol=2) for _, cls in self._classes.items(): for method in cls.methods: method_key = get_method_key(method) self._methods[method_key] = method for block in method.blocks: self._blocks_to_methods[block] = method for stmt in block.statements: self._stmts_to_blocks[stmt] = block self._stmts_to_classes[stmt] = cls
def test_exceptions1(): jar = os.path.join(test_samples_folder, "exceptions1.jar") lifter = Lifter(jar) mm = lifter.classes["exceptions1.Main"].methods[1] assert mm.basic_cfg[mm.blocks[0]] == [mm.blocks[1], mm.blocks[2]] assert len(mm.exceptional_preds) == 1 preds = mm.exceptional_preds[mm.blocks[18]] for i, block in enumerate(mm.blocks): if i in [0, 1, 2, 17, 18, 19]: assert not block in preds elif i in [3, 4, 5, 14, 15, 16]: assert block in preds
def test_exceptions1(): jar = os.path.join(test_samples_folder, "exceptions1.jar") lifter = Lifter(jar) mm = lifter.classes["exceptions1.Main"].methods[1] nose.tools.assert_equal(mm.basic_cfg[mm.blocks[0]], [mm.blocks[1], mm.blocks[2]]) nose.tools.assert_true(len(mm.exceptional_preds) == 1) preds = mm.exceptional_preds[mm.blocks[18]] for i, block in enumerate(mm.blocks): if i in [0, 1, 2, 17, 18, 19]: nose.tools.assert_false(block in preds) elif i in [3, 4, 5, 14, 15, 16]: nose.tools.assert_true(block in preds)
def test_textcrunchr1(): jar = os.path.join(test_samples_folder_private, "textcrunchr_1.jar") additional_jar_roots = [ os.path.join(test_samples_folder_private, "textcrunchr_libs") ] lifter = Lifter(jar, additional_jar_roots=additional_jar_roots) tstr = str( lifter. classes['com.cyberpointllc.stac.textcrunchr.CharacterCountProcessor']) tokens = [ "getName", "Character Count", ">10,000 characters", "new char[10000]", "process", "com.cyberpointllc.stac.textcrunchr.TCResult" ] for t in tokens: nose.tools.assert_in(t, tstr)
def __init__(self, path, additional_jars=None, additional_jar_roots=None, main_class=None, **kwargs): if not pysoot: raise ImportError( 'Cannot import PySoot. The Soot backend requires PySoot to function. ' 'Please install PySoot first.') if kwargs.get('has_memory', False): raise CLEError( 'The parameter "has_memory" must be False for Soot backend.') super(Soot, self).__init__(path, has_memory=False, **kwargs) if not main_class: # parse main_class from the manifest self.manifest = self.get_manifest() main_class = self.manifest.get('Main-Class', None) # load the classes pysoot_lifter = Lifter( path, additional_jars=additional_jars, additional_jar_roots=additional_jar_roots, # main_class=main_class, ) self._classes = pysoot_lifter.classes # find entry method try: main_method_descriptor = SootMethodDescriptor.from_method( next(self.get_method("main", main_class))) entry = SootAddressDescriptor(main_method_descriptor, 0, 0) except CLEError: _l.warning( 'Failed to identify the entry (the Main method) of this JAR.') entry = None self._entry = entry self.os = 'javavm' self.rebase_addr = None self.set_arch(archinfo.arch_from_id('soot'))
def test_android1(): # TODO this requires a copy of the Sdk, I am not sure how can we put it in CI apk = os.path.join(test_samples_folder, "android1.apk") # TODO fix path to the Android Sdk to make CI happy lifter = Lifter(apk, input_format="apk", android_sdk=os.path.join(os.path.expanduser("~"), "Android/Sdk/platforms/")) subc = lifter.soot_wrapper.getSubclassesOf("java.lang.Object") nose.tools.assert_in("com.example.antoniob.android1.MainActivity", subc) main_activity = lifter.classes[ "com.example.antoniob.android1.MainActivity"] tstr = str(main_activity) tokens = [ "onCreate", "ANDROID1", "TAG", "Random", "android.os.Bundle", "34387" ] for t in tokens: nose.tools.assert_in(t, tstr)
def test_android1(): # TODO consider adding Android Sdk in the CI server sdk_path = os.path.join(os.path.expanduser("~"), "Android/Sdk/platforms/") if not os.path.exists(sdk_path): l.warning( "cannot run test_android1 since there is no Android SDK folder") return apk = os.path.join(test_samples_folder, "android1.apk") lifter = Lifter(apk, input_format="apk", android_sdk=sdk_path) subc = lifter.soot_wrapper.getSubclassesOf("java.lang.Object") assert "com.example.antoniob.android1.MainActivity" in subc main_activity = lifter.classes[ "com.example.antoniob.android1.MainActivity"] tstr = str(main_activity) tokens = [ "onCreate", "ANDROID1", "TAG", "Random", "android.os.Bundle", "34387" ] for t in tokens: assert t in tstr
def test_textcrunchr1(): if not os.path.exists(test_samples_folder_private): l.warning( "cannot run test_textcrunchr1 since there is no binaries-private folder" ) return jar = os.path.join(test_samples_folder_private, "textcrunchr_1.jar") additional_jar_roots = [ os.path.join(test_samples_folder_private, "textcrunchr_libs") ] lifter = Lifter(jar, additional_jar_roots=additional_jar_roots) tstr = str( lifter. classes['com.cyberpointllc.stac.textcrunchr.CharacterCountProcessor']) tokens = [ "getName", "Character Count", ">10,000 characters", "new char[10000]", "process", "com.cyberpointllc.stac.textcrunchr.TCResult" ] for t in tokens: assert t in tstr
def start(self, sender, ran_fun=lambda *args: None, lifter=None): if not self.lifter: if lifter is not None: self.lifter = lifter if self.lifter is None: log.debug("Building Lifter") self.lifter = Lifter( self.config['apk_path'], input_format="apk", android_sdk=self.config['android_sdk_platforms']) log.debug("Lifter pickled in " + LIFTER_PICKLE) self.p = turi.Project(self.apk_path, input_format='apk', android_sdk=self.config['android_sdk_platforms'], lifter=self.lifter) if not self.nf: self.nf = NodeFilter(self.config, lifter=self.lifter) self.find_sweet_spots(sender, ran_fun) self.clean_ss() return self.sweet_spots, self.sweet_objs
def _simple2_tests(ir_format): jar = os.path.join(test_samples_folder, "simple2.jar") lifter = Lifter(jar, ir_format=ir_format) cc = lifter.classes['simple2.Class1'] tstr = str(cc) tokens = [ "new int", "instanceof simple2.Class1", "parameter0", "Caught", "Throw", " = 2", "goto", "switch", "START!", "valueOf" ] for t in tokens: assert t in tstr # Phi instructions only exist in SSA form (shimple) if ir_format == "jimple": assert "Phi" not in tstr elif ir_format == "shimple": assert "Phi" in tstr # "<pysoot" in a line (outside comments) means that a str is missing (and therefore repr was used) for line in tstr.split("\n"): line = line.split("//")[0] assert "<pysoot" not in line
def test_ipc_options(): jar = os.path.join(test_samples_folder, "simple2.jar") for split_results in [0, 10, 100]: lifter = Lifter(jar) cc = lifter.classes['simple2.Class1'] tstr = str(cc) tokens = [ "new int", "instanceof simple2.Class1", "parameter0", "Caught", "Throw", " = 2", "goto", "switch", "START!", "valueOf" ] for t in tokens: assert t in tstr sw = lifter.soot_wrapper res = sw.get_classes( _ipc_options={ 'return_result': False, 'return_pickle': False, 'save_pickle': None, 'split_results': split_results }) assert res is None res, pres = sw.get_classes( _ipc_options={ 'return_result': True, 'return_pickle': True, 'save_pickle': None, 'split_results': split_results }) res2 = pickle.loads(pres) compare_code(str(res['simple2.Class1']), str(res2['simple2.Class1'])) res = sw.get_classes( _ipc_options={ 'return_result': True, 'return_pickle': False, 'save_pickle': None, 'split_results': split_results }) compare_code(str(res['simple2.Class1']), tstr) res, pres = sw.get_classes( _ipc_options={ 'return_result': False, 'return_pickle': True, 'save_pickle': None, 'split_results': split_results }) assert res is None assert pres is not None classes = [ u'simple2.Interface2', u'simple2.Interface1', u'simple2.Class1$Inner1', u'simple2.Class2', u'simple2.Class1' ] try: fname = tempfile.mktemp() res1 = sw.get_classes( _ipc_options={ 'return_result': False, 'return_pickle': False, 'save_pickle': fname, 'split_results': split_results }) assert res1 is None res2 = pickle.load(open(fname, "rb")) compare_code(str(res2['simple2.Class1']), tstr) finally: os.unlink(fname) try: fname = tempfile.mktemp() res1 = sw.get_classes( _ipc_options={ 'return_result': True, 'return_pickle': False, 'save_pickle': fname, 'split_results': split_results }) assert res1 is not None res2 = pickle.load(open(fname, "rb")) compare_code(str(res1['simple2.Class1']), str(res2['simple2.Class1'])) finally: os.unlink(fname) try: fname = tempfile.mktemp() jar = os.path.join(test_samples_folder, "simple2.jar") Lifter(jar, save_to_file=fname) res2 = pickle.load(open(fname, "rb")) assert set(classes) == set(res2.keys()) finally: os.unlink(fname)
def __init__(self, path, entry_point=None, entry_point_params=(), input_format=None, additional_jars=None, additional_jar_roots=None, jni_libs_ld_path=None, jni_libs=None, android_sdk=None, **kwargs): if not pysoot: raise ImportError( 'Cannot import PySoot. The Soot backend requires PySoot.') if kwargs.get('has_memory', False): raise CLEError( 'The parameter "has_memory" must be False for Soot backend.') super(Soot, self).__init__(path, has_memory=False, **kwargs) # load the classes l.debug("Lifting to Soot IR ...") start_time = time.time() pysoot_lifter = Lifter(path, input_format=input_format, android_sdk=android_sdk, additional_jars=additional_jars, additional_jar_roots=additional_jar_roots) end_time = time.time() l.debug("Lifting completed in %ds", round(end_time - start_time, 2)) self._classes = pysoot_lifter.classes # find entry method if entry_point: try: ep_method = self.get_soot_method(entry_point, params=entry_point_params) ep_method_descriptor = SootMethodDescriptor.from_soot_method( ep_method) self._entry = SootAddressDescriptor(ep_method_descriptor, 0, 0) l.debug("Entry point set to %s", self._entry) except CLEError: l.warning("Couldn't find entry point %s.", entry_point) self._entry = None self.os = 'javavm' self.rebase_addr = None self.set_arch(ArchSoot()) if jni_libs: # native libraries are getting loaded by adding them as a dependency of this object self.deps += [jni_libs] if type(jni_libs) in (str, bytes) else jni_libs # if available, add additional load path(s) if jni_libs_ld_path: path_list = [jni_libs_ld_path] if type(jni_libs_ld_path) in ( str, bytes) else jni_libs_ld_path self.extra_load_path += path_list self.jni_support = True else: self.jni_support = False
def setup_lifter(self): # pysoot self.lifter = Lifter(self.apk_path, input_format="apk", android_sdk=self.android_sdk)
sys.stderr.flush() print "*" * 1000 print "START\n\n" print datetime.datetime.now() if len(sys.argv) > 1: finput = sys.argv[1] else: finput = "/home/ubuntu/com.facebook.orca.apk" logging.basicConfig( format='%(levelname)-7s | %(asctime)-23s | %(name)-8s | %(message)s', level=logging.DEBUG) lifter = Lifter(input_file=finput, input_format="apk", android_sdk=os.path.join(os.path.expanduser("~"), "Android/Sdk/platforms/")) print lifter.soot_wrapper.get_client_std()[-3000:] print "*" * 1000 print "END\n\n" print datetime.datetime.now() sys.stdout.flush() sys.stderr.flush() import IPython IPython.embed() import cPickle as pickle
def create_lifter(self): log.info("Creating Lifter") self.lifter = Lifter(self.config['apk_path'], input_format="apk", android_sdk=self.config['android_sdk_platforms'])
sys.exit(0) with open(config_path) as fp: config = json.load(fp) lifter = None leaves = True if methods: leaves = False methods = map(lambda x: ast.literal_eval(x.replace('\\', '')), methods) elif clss: leaves = False lifter = Lifter(config['apk_path'], input_format="apk", android_sdk=config['android_sdk_platforms']) classes = [c for c in lifter.classes.values() if c.name in clss] methods = [[clx.name, m.name, list(m.params), m.ret] for clx in classes for m in clx.methods] fh = FridaHooker(config) fh.start(leaves=leaves, lifter=lifter, to_hook=methods, fast_hook=True) # , force_hook=True) last = [] print "Now you can interact with the app and see which hooked methods are called." print "Press ctrl+c to exit." while True: if fh.last_methods_called: for m in fh.last_methods_called if not show_vals else fh.last_methods_instances:
def test_hierarchy(): jar = os.path.join(test_samples_folder, "simple2.jar") lifter = Lifter(jar) test_subc = ["simple2.Class2", "simple2.Class1", "java.lang.System"] subc = lifter.soot_wrapper.getSubclassesOf("java.lang.Object") assert all([c in subc for c in test_subc])