def getPackageUid(self, package_name): logger.debug("Getting UID of the package [%s]..." % package_name) uid = -1 if not self.is_alive(): logger.error("The device [%s] is not running!" % self.device_name) return uid if package_name == "": logger.warning("The name of the package is not specified!") return uid uid_lines = subprocess.check_output([ 'adb', '-s', self.device_name, 'shell', 'cat', '/data/system/packages.list' ]) lines = uid_lines.splitlines() for i in range( 0, len(lines)): #first line just announces the list of devices words = lines[i].split(' ') if words[0].strip() == package_name: uid = int(words[1].strip()) break logger.debug("The UID of the package [%s] is [%d]" % (package_name, uid)) return uid
def _convertDex2JarFiles(self, converter, dexFilesRootDir, dexFilesRelativePaths, jarFilesRootDir, proceedOnError=True): jarFilesRelativePaths = [] for dexFileRelativePath in dexFilesRelativePaths: dexFilePath = os.path.join(dexFilesRootDir, dexFileRelativePath) jarFileRelativePath = os.path.splitext( dexFileRelativePath)[0] + ".jar" jarFilePath = os.path.join(jarFilesRootDir, jarFileRelativePath) try: converter.convertDex2Jar(dexFilePath, jarFilePath, overwrite=True) except Dex2JarConvertionError as e: if proceedOnError: logger.warning("Cannot convert [%s] to [%s]. %s" ( dexFilePath, jarFilePath, e.msg)) continue else: raise jarFilesRelativePaths.append(jarFileRelativePath) return jarFilesRelativePaths
def addInvokePath(self, src, through, dst): src_class_name, src_method_name, src_descriptor = src dst_class_name, dst_method_name, dst_descriptor = dst through_class_name, through_method_name, through_descriptor = through key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, through_class_name, through_method_name, through_descriptor, POSTFIX_REFL_INVOKE) n1 = self._get_existed_node(key) if n1 == None: logger.warning("Something wrong has happened! Could not find invoke Node in Graph with key [%s]" % str(key)) return n2 = self._get_node(NODE_METHOD, (dst_class_name, dst_method_name, dst_descriptor)) n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) self.G.add_edge(n1.id, n2.id) #check if called method calls protected feature data = "%s-%s-%s" % (dst_class_name, dst_method_name, dst_descriptor) if data in DVM_PERMISSIONS_BY_API_CALLS: logger.info("BINGOOOOOOO! The protected method is called through reflection!") perm = DVM_PERMISSIONS_BY_API_CALLS[ data ] key1 = "%s %s %s %s %s %s %s %s" % (through_class_name, through_method_name, through_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_PERM, perm) n3 = self._get_node(NODE_FAKE_PERMISSION, key1, perm, False) n3.set_attribute(ATTR_CLASS_NAME, dst_class_name) n3.set_attribute(ATTR_METHOD_NAME, dst_method_name) n3.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) n3.set_attribute(ATTR_PERM_NAME, perm) n3.set_attribute(ATTR_PERM_LEVEL, MANIFEST_PERMISSIONS[ perm ][0]) self.G.add_edge(n2.id, n3.id)
def _instrFilesWithEmma(self, instrumenter, jarFilesRootDir, jarFilesRelativePaths, instrJarsRootDir, coverageMetadataFile, proceedOnError=True): instrJarFilesRelativePaths = [] for jarFileRelativePath in jarFilesRelativePaths: jarFileAbsPath = os.path.join(jarFilesRootDir, jarFileRelativePath) instrJarRelativeDir = jarFileRelativePath[:jarFileRelativePath. rfind("/") + 1] instrJarFullDir = os.path.join(instrJarsRootDir, instrJarRelativeDir) try: instrumenter.instrumentJarWithEmma( jarFile=jarFileAbsPath, outputFolder=instrJarFullDir, emmaMetadataFile=coverageMetadataFile) except EmmaCannotInstrumentException as e: if proceedOnError: logger.warning("Cannot instrument [%s]. %s" ( jarFileAbsPath, e.msg)) continue else: raise instrJarFilesRelativePaths.append(jarFileRelativePath) return instrJarFilesRelativePaths
def _convertJar2DexWithInstr(self, converter, instrJarsRootDir, instrJarFilesRelativePaths, finalDexFilesRootDir, proceedOnError): instrDexFilesRelativePaths = [] for jarFileRelativePath in instrJarFilesRelativePaths: jarFileAbsPath = os.path.join(instrJarsRootDir, jarFileRelativePath) dexFileRelativePath = os.path.splitext(jarFileRelativePath)[0] + ".dex" dexFileAbsPath = os.path.join(finalDexFilesRootDir, dexFileRelativePath) #we instrument main file with auxiliary classes print "jarFileRelativePath: " + jarFileRelativePath try: withFiles = [] #we compile main file with additional instrumentation files #hack: emma copies instrumented jar files into lib folder if jarFileRelativePath == "classes.jar": emmaDevicePath = os.path.join(self.config.getEmmaDir(), self.config.getEmmaDeviceJar()) withFiles.append(self.config.getAndroidSpecificInstrumentationClassesPath()) withFiles.append(emmaDevicePath) converter.convertJar2Dex(jarFile=jarFileAbsPath, dexFile=dexFileAbsPath, withFiles=withFiles, overwrite=True) except Jar2DexConvertionError as e: if proceedOnError: logger.warning("Cannot instrument [%s]. %s" % (jarFileAbsPath, e.msg)) continue else: raise instrDexFilesRelativePaths.append(dexFileRelativePath) return instrDexFilesRelativePaths
def start_activity(self, package_name, activity_name): #adb shell am start -n com.package.name/com.package.name.ActivityName logger.debug("Starting activity [%s] of the package [%s]..." % (package_name, activity_name)) if not self.is_alive(): logger.error("The device [%s] is not running!" % self.device_name) return if not package_name: logger.warning("The name of the package is not specified!") return if not activity_name: logger.warning("The name of the activity is not specified!") return run_string = package_name + '/' + activity_name try: with open(os.devnull, 'w') as f_null: subprocess.check_call([ 'adb', '-s', self.device_name, 'shell', 'am start', '-n', run_string ], stderr=f_null) except subprocess.CalledProcessError: logger.error("Could not run activity!") return
def analyseStadynaMsg(device, filesDir, stadynaAnalyser, stadynaMsg): logger.debug("Analysing obtained message...") operation = int(stadynaMsg.get(consts.JSON_OPERATION)) if operation == consts.OP_CLASS_NEW_INSTANCE: logger.debug("Obtained message is OP_CLASS_NEW_INSTANCE!") processNewInstanceMsg(stadynaAnalyser, stadynaMsg) return if operation == consts.OP_METHOD_INVOKE: logger.debug("Obtained message is OP_METHOD_INVOKE!") processInvokeMsg(stadynaAnalyser, stadynaMsg) return if operation == consts.OP_DEX_LOAD: logger.debug("Obtained message is OP_DEX_LOAD!") processDexLoadMsg(device, filesDir, stadynaAnalyser, stadynaMsg) return logger.warning("Program does not contain routine to process message: [%d]!" % operation)
def _convertDex2JarFiles(self, converter, dexFilesRootDir, dexFilesRelativePaths, jarFilesRootDir, proceedOnError=True): jarFilesRelativePaths = [] for dexFileRelativePath in dexFilesRelativePaths: dexFilePath = os.path.join(dexFilesRootDir, dexFileRelativePath) jarFileRelativePath = os.path.splitext(dexFileRelativePath)[0] + ".jar" jarFilePath = os.path.join(jarFilesRootDir, jarFileRelativePath) try: converter.convertDex2Jar(dexFilePath, jarFilePath, overwrite=True) except Dex2JarConvertionError as e: if proceedOnError: logger.warning("Cannot convert [%s] to [%s]. %s" (dexFilePath, jarFilePath, e.msg)) continue else: raise jarFilesRelativePaths.append(jarFileRelativePath) return jarFilesRelativePaths
def get_file(self, what, to_dir): logger.debug("Coping file [%s] from device to directory [%s]..." % (what, to_dir)) if not self.is_alive(): logger.error("The device [%s] is not running!" % self.device_name) return False if what == "": logger.warning("The name of the file to download is not specified!") return False try: with open(os.devnull, 'w') as f_null: subprocess.check_call(["adb", "-s", self.device_name, "pull", what, to_dir], stderr=f_null) except subprocess.CalledProcessError: logger.error("Could not download file [%s] from the device!" % what) return False logger.debug("File [%s] is downloaded!" % what) return True
def install_package(self, apk_path): logger.debug("Installing application [%s]..." % apk_path) if not self.is_alive(): logger.error("The device [%s] is not running!" % str(self.device_name)) return False if apk_path == "": logger.warning("The path to the application to install is not specified!") return False try: with open(os.devnull, 'w') as f_null: subprocess.check_call(["adb", "-s", self.device_name, "install", "-r", apk_path], stderr=f_null) except subprocess.CalledProcessError: logger.error("Could not install application [%s] on the device!" % apk_path) return False logger.debug("Application [%s] is installed!" % apk_path) return True
def _convertJar2DexWithInstr(self, converter, instrJarsRootDir, instrJarFilesRelativePaths, finalDexFilesRootDir, proceedOnError): instrDexFilesRelativePaths = [] for jarFileRelativePath in instrJarFilesRelativePaths: jarFileAbsPath = os.path.join(instrJarsRootDir, jarFileRelativePath) dexFileRelativePath = os.path.splitext( jarFileRelativePath)[0] + ".dex" dexFileAbsPath = os.path.join(finalDexFilesRootDir, dexFileRelativePath) #we instrument main file with auxiliary classes print "jarFileRelativePath: " + jarFileRelativePath try: withFiles = [] #we compile main file with additional instrumentation files #hack: emma copies instrumented jar files into lib folder if jarFileRelativePath == "classes.jar": emmaDevicePath = os.path.join( self.config.getEmmaDir(), self.config.getEmmaDeviceJar()) withFiles.append( self.config. getAndroidSpecificInstrumentationClassesPath()) withFiles.append(emmaDevicePath) converter.convertJar2Dex(jarFile=jarFileAbsPath, dexFile=dexFileAbsPath, withFiles=withFiles, overwrite=True) except Jar2DexConvertionError as e: if proceedOnError: logger.warning("Cannot instrument [%s]. %s" % (jarFileAbsPath, e.msg)) continue else: raise instrDexFilesRelativePaths.append(dexFileRelativePath) return instrDexFilesRelativePaths
def _instrFilesWithEmma(self, instrumenter, jarFilesRootDir, jarFilesRelativePaths, instrJarsRootDir, coverageMetadataFile, proceedOnError=True): instrJarFilesRelativePaths = [] for jarFileRelativePath in jarFilesRelativePaths: jarFileAbsPath = os.path.join(jarFilesRootDir, jarFileRelativePath) instrJarRelativeDir = jarFileRelativePath[:jarFileRelativePath.rfind("/")+1] instrJarFullDir = os.path.join(instrJarsRootDir, instrJarRelativeDir) try: instrumenter.instrumentJarWithEmma(jarFile=jarFileAbsPath, outputFolder=instrJarFullDir, emmaMetadataFile=coverageMetadataFile) except EmmaCannotInstrumentException as e: if proceedOnError: logger.warning("Cannot instrument [%s]. %s" (jarFileAbsPath, e.msg)) continue else: raise instrJarFilesRelativePaths.append(jarFileRelativePath) return instrJarFilesRelativePaths
def get_package_uid(self, package_name): logger.debug("Getting UID of the package [%s]..." % package_name) uid = -1 if not self.is_alive(): logger.error("The device [%s] is not running!" % self.device_name) return uid if package_name == "": logger.warning("The name of the package is not specified!") return uid uid_lines = subprocess.check_output(['adb', '-s', self.device_name, 'shell', 'cat', '/data/system/packages.list']) lines = uid_lines.splitlines() for i in range(0,len(lines)): #first line just announces the list of devices words = lines[i].split(' ') if words[0].strip() == package_name: uid = int(words[1].strip()) break logger.debug("The UID of the package [%s] is [%d]" % (package_name, uid)) return uid
def initAlreadyInstrApkEnv(self, pathToInstrApk, resultsDir, pathToInstrManifestFile=None): if not apk_utils.checkInputApkFile(pathToInstrApk): logger.error("Provided file [%s] is not a valid apk file!" % pathToInstrApk) return if not os.path.isdir(resultsDir): logger.error("Provided path to results dir [%s] do not point to dir!" % resultsDir) return coverageMetadataFolderPath = os.path.join(resultsDir, self.config.getCoverageMetadataRelativeDir()) if not os.path.isdir(coverageMetadataFolderPath): logger.error("In the results dir [%s] there is no folder with coverage metadata!" % resultsDir) return self.coverageMetadataFolder = coverageMetadataFolderPath if self.config.getCoverageMetadataFilename() not in os.listdir(coverageMetadataFolderPath): logger.error("Cannot find metadata filename in the coverage metadata folder: %s!" % self.coverageMetadataFolder) return self.coverageMetadataFile = os.path.join(self.coverageMetadataFolder, self.config.getCoverageMetadataFilename()) #by default trying to look for a file in the if pathToInstrManifestFile: androidManifestPath = pathToInstrManifestFile else: androidManifestPath = os.path.join(resultsDir, "AndroidManifest.xml") if not os.path.isfile(androidManifestPath): logger.warning("Path [%s] is not pointing to a real file! Leaving pointer to AndroidManifest.xml empty!" % androidManifestPath) return self.instrumentedApk = pathToInstrApk self.apkResultsDir = resultsDir self.runtimeReportsRootDir = self._createDir(resultsDir, self.config.getRuntimeReportsRelativeDir(), False, False) self.androidManifestFile = androidManifestPath self.androidManifest = AndroidManifest(self.androidManifestFile) self.packageName = self.androidManifest.getInstrumentationTargetPackage() self.runnerName = self.androidManifest.getInstrumentationRunnerName() self._bboxStateMachine.start(STATE_VALID_SETTINGS_PROVIDED)
def addInvokePath(self, src, through, dst): src_class_name, src_method_name, src_descriptor = src dst_class_name, dst_method_name, dst_descriptor = dst through_class_name, through_method_name, through_descriptor = through key = "%s %s %s %s %s %s %s" % ( src_class_name, src_method_name, src_descriptor, through_class_name, through_method_name, through_descriptor, POSTFIX_REFL_INVOKE) n1 = self._get_existed_node(key) if n1 == None: logger.warning( "Something wrong has happened! Could not find invoke Node in Graph with key [%s]" % str(key)) return n2 = self._get_node(NODE_METHOD, (dst_class_name, dst_method_name, dst_descriptor)) n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) self.G.add_edge(n1.id, n2.id) #check if called method calls protected feature data = "%s-%s-%s" % (dst_class_name, dst_method_name, dst_descriptor) if data in DVM_PERMISSIONS_BY_API_CALLS: logger.info( "BINGOOOOOOO! The protected method is called through reflection!" ) perm = DVM_PERMISSIONS_BY_API_CALLS[data] key1 = "%s %s %s %s %s %s %s %s" % ( through_class_name, through_method_name, through_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_PERM, perm) n3 = self._get_node(NODE_FAKE_PERMISSION, key1, perm, False) n3.set_attribute(ATTR_CLASS_NAME, dst_class_name) n3.set_attribute(ATTR_METHOD_NAME, dst_method_name) n3.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) n3.set_attribute(ATTR_PERM_NAME, perm) n3.set_attribute(ATTR_PERM_LEVEL, MANIFEST_PERMISSIONS[perm][0]) self.G.add_edge(n2.id, n3.id)
def check_spell(doc): #SpellChecker words = [token.text for token in doc] misspelled = spell.unknown(words) misspelled errors = [] if misspelled: for misspell in misspelled: if misspell == ' ' or misspell in words: logger.warning(f'word "{misspell}" skipped in spelling correction') continue correct = spell.correction(misspell) tip = spell.candidates(misspell) misspell_text = f'Esta palavra... "{misspell}"' error = {'match': misspell_text,'correct': correct, 'tip': 'Você quis dizer "{}"?'.format(correct)} errors.append(error) return errors
def start_activity(self, package_name, activity_name): #adb shell am start -n com.package.name/com.package.name.ActivityName logger.debug("Starting activity [%s] of the package [%s]..." % (package_name, activity_name)) if not self.is_alive(): logger.error("The device [%s] is not running!" % self.device_name) return if not package_name: logger.warning("The name of the package is not specified!") return if not activity_name: logger.warning("The name of the activity is not specified!") return run_string = package_name + '/' + activity_name try: with open(os.devnull, 'w') as f_null: subprocess.check_call(['adb', '-s', self.device_name, 'shell', 'am start', '-n', run_string], stderr=f_null) except subprocess.CalledProcessError: logger.error("Could not run activity!") return
def get_file(self, what, to_dir): logger.debug("Coping file [%s] from device to directory [%s]..." % (what, to_dir)) if not self.is_alive(): logger.error("The device [%s] is not running!" % self.device_name) return False if what == "": logger.warning( "The name of the file to download is not specified!") return False try: with open(os.devnull, 'w') as f_null: subprocess.check_call( ["adb", "-s", self.device_name, "pull", what, to_dir], stderr=f_null) except subprocess.CalledProcessError: logger.error("Could not download file [%s] from the device!" % what) return False logger.debug("File [%s] is downloaded!" % what) return True
def install_package(self, apk_path): logger.debug("Installing application [%s]..." % apk_path) if not self.is_alive(): logger.error("The device [%s] is not running!" % str(self.device_name)) return False if apk_path == "": logger.warning( "The path to the application to install is not specified!") return False try: with open(os.devnull, 'w') as f_null: subprocess.check_call( ["adb", "-s", self.device_name, "install", "-r", apk_path], stderr=f_null) except subprocess.CalledProcessError: logger.error("Could not install application [%s] on the device!" % apk_path) return False logger.debug("Application [%s] is installed!" % apk_path) return True
def initAlreadyInstrApkEnv(self, pathToInstrApk, resultsDir, pathToInstrManifestFile=None): if not apk_utils.checkInputApkFile(pathToInstrApk): logger.error("Provided file [%s] is not a valid apk file!" % pathToInstrApk) return if not os.path.isdir(resultsDir): logger.error( "Provided path to results dir [%s] do not point to dir!" % resultsDir) return coverageMetadataFolderPath = os.path.join( resultsDir, self.config.getCoverageMetadataRelativeDir()) if not os.path.isdir(coverageMetadataFolderPath): logger.error( "In the results dir [%s] there is no folder with coverage metadata!" % resultsDir) return self.coverageMetadataFolder = coverageMetadataFolderPath if self.config.getCoverageMetadataFilename() not in os.listdir( coverageMetadataFolderPath): logger.error( "Cannot find metadata filename in the coverage metadata folder: %s!" % self.coverageMetadataFolder) return self.coverageMetadataFile = os.path.join( self.coverageMetadataFolder, self.config.getCoverageMetadataFilename()) #by default trying to look for a file in the if pathToInstrManifestFile: androidManifestPath = pathToInstrManifestFile else: androidManifestPath = os.path.join(resultsDir, "AndroidManifest.xml") if not os.path.isfile(androidManifestPath): logger.warning( "Path [%s] is not pointing to a real file! Leaving pointer to AndroidManifest.xml empty!" % androidManifestPath) return self.instrumentedApk = pathToInstrApk self.apkResultsDir = resultsDir self.runtimeReportsRootDir = self._createDir( resultsDir, self.config.getRuntimeReportsRelativeDir(), False, False) self.androidManifestFile = androidManifestPath self.androidManifest = AndroidManifest(self.androidManifestFile) self.packageName = self.androidManifest.getInstrumentationTargetPackage( ) self.runnerName = self.androidManifest.getInstrumentationRunnerName() self._bboxStateMachine.start(STATE_VALID_SETTINGS_PROVIDED)
def analyseFile(self, vmx, apk): vm = vmx.get_vm() self.androGuardObjects.append((apk, vm, vmx)) # self.internal_methods.extend(vm.get_methods()) #creating real internal nodes internal_called_methods = vmx.get_tainted_packages( ).stadyna_get_internal_called_methods() for method in internal_called_methods: class_name, method_name, descriptor = method nodeType = None if method_name == "<clinit>": nodeType = NODE_STATIC_INIT elif method_name == "<init>": nodeType = NODE_CONSTRUCTOR else: nodeType = NODE_METHOD n = self._get_node(nodeType, (class_name, method_name, descriptor)) n.set_attribute(ATTR_CLASS_NAME, class_name) n.set_attribute(ATTR_METHOD_NAME, method_name) n.set_attribute(ATTR_DESCRIPTOR, descriptor) self.G.add_node(n.id) #creating real edges (nodes are already there) #currently we are working only with internal packages. for j in vmx.get_tainted_packages().get_internal_packages(): src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager()) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager()) n1 = self._get_existed_node( (src_class_name, src_method_name, src_descriptor)) # n1.set_attribute(ATTR_CLASS_NAME, src_class_name) # n1.set_attribute(ATTR_METHOD_NAME, src_method_name) # n1.set_attribute(ATTR_DESCRIPTOR, src_descriptor) n2 = self._get_existed_node( (dst_class_name, dst_method_name, dst_descriptor)) # n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) # n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) # n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) self.G.add_edge(n1.id, n2.id) #adding fake class nodes for method in internal_called_methods: src_class_name, src_method_name, src_descriptor = method if src_method_name == "<init>" or src_method_name == "<clinit>": n1 = self._get_existed_node( (src_class_name, src_method_name, src_descriptor)) n2 = self._get_node(NODE_FAKE_CLASS, src_class_name, None, False) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) if src_method_name == "<clinit>": self.G.add_edge(n1.id, n2.id) elif src_method_name == "<init>": self.G.add_edge(n2.id, n1.id) #real (external) reflection invoke nodes reflection_invoke_paths = analysis.seccon_get_invoke_method_paths(vmx) for j in reflection_invoke_paths: src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager()) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager()) n1 = self._get_existed_node( (src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning( "Cannot find the node [%s], where reflection invoke is called!" % (src_class_name, src_method_name, src_descriptor)) continue key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_REFL_INVOKE) n2 = self._get_node(NODE_REFL_INVOKE, key, LABEL_REFL_INVOKE, True) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) n2.set_attribute(ATTR_METHOD_NAME, src_method_name) n2.set_attribute(ATTR_DESCRIPTOR, src_descriptor) self.G.add_edge(n1.id, n2.id) #real (external) reflection new instance nodes reflection_newInstance_paths = analysis.seccon_get_newInstance_method_paths( vmx) for j in reflection_newInstance_paths: src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager()) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager()) n1 = self._get_existed_node( (src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning( "Cannot find the node [%s], where reflection new instance is called!" % (src_class_name, src_method_name, src_descriptor)) continue key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_REFL_NEWINSTANCE) n2 = self._get_node(NODE_REFL_NEWINSTANCE, key, LABEL_REFL_NEWINSTANCE, True) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) n2.set_attribute(ATTR_METHOD_NAME, src_method_name) n2.set_attribute(ATTR_DESCRIPTOR, src_descriptor) self.G.add_edge(n1.id, n2.id) #adding fake entry points if apk != None: for i in apk.get_activities(): j = bytecode.FormatClassToJava(i) n1 = self._get_existed_node( (j, "onCreate", "(Landroid/os/Bundle;)V")) if n1 != None: key = "%s %s %s %s" % (j, "onCreate", "(Landroid/os/Bundle;)V", POSTFIX_ACTIVITY) n2 = self._get_node(NODE_FAKE_ACTIVITY, key, LABEL_ACTIVITY, False) self.G.add_edge(n2.id, n1.id) self.entry_nodes.append(n1.id) for i in apk.get_services(): j = bytecode.FormatClassToJava(i) n1 = self._get_existed_node((j, "onCreate", "()V")) if n1 != None: key = "%s %s %s %s" % (j, "onCreate", "()V", POSTFIX_SERVICE) n2 = self._get_node(NODE_FAKE_SERVICE, key, LABEL_SERVICE, False) self.G.add_edge(n2.id, n1.id) self.entry_nodes.append(n1.id) for i in apk.get_receivers(): j = bytecode.FormatClassToJava(i) n1 = self._get_existed_node( (j, "onReceive", "(Landroid/content/Context;Landroid/content/Intent;)V")) if n1 != None: key = "%s %s %s %s" % ( j, "onReceive", "(Landroid/content/Context;Landroid/content/Intent;)V", POSTFIX_RECEIVER) n2 = self._get_node(NODE_FAKE_SERVICE, key, LABEL_RECEIVER, False) self.G.add_edge(n2.id, n1.id) self.entry_nodes.append(n1.id) #fake permissions list_permissions = vmx.stadyna_get_permissions([]) for x in list_permissions: for j in list_permissions[x]: if isinstance(j, PathVar): continue src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager()) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager()) n1 = self._get_existed_node( (src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning( "Cannot find node [%s %s %s] for permission [%s]!" % (src_class_name, src_method_name, src_descriptor, x)) continue #SOURCE, DEST, POSTFIX, PERMISSION_NAME key = "%s %s %s %s %s %s %s %s" % ( src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_PERM, x) n2 = self._get_node(NODE_FAKE_PERMISSION, key, x, False) n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) n2.set_attribute(ATTR_PERM_NAME, x) n2.set_attribute(ATTR_PERM_LEVEL, MANIFEST_PERMISSIONS[x][0]) self.G.add_edge(n1.id, n2.id) #fake DexClassLoader nodes dyn_code_loading = analysis.seccon_get_dyncode_loading_paths(vmx) for j in dyn_code_loading: src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager()) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager()) n1 = self._get_existed_node( (src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning( "Cannot find dexload node [%s]!" % (src_class_name, src_method_name, src_descriptor)) continue key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_DEXLOAD) n2 = self._get_node(NODE_FAKE_DEXLOAD, key, LABEL_DEXLOAD, False) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) n2.set_attribute(ATTR_METHOD_NAME, src_method_name) n2.set_attribute(ATTR_DESCRIPTOR, src_descriptor) self.G.add_edge(n1.id, n2.id) # Specific Java/Android library for c in vm.get_classes(): #if c.get_superclassname() == "Landroid/app/Service;" : # n1 = self._get_node( c.get_name(), "<init>", "()V" ) # n2 = self._get_node( c.get_name(), "onCreate", "()V" ) # self.G.add_edge( n1.id, n2.id ) if c.get_superclassname( ) == "Ljava/lang/Thread;" or c.get_superclassname( ) == "Ljava/util/TimerTask;": for i in vm.get_method("run"): if i.get_class_name() == c.get_name(): n1 = self._get_node(NODE_METHOD, (i.get_class_name(), i.get_name(), i.get_descriptor())) n2 = self._get_node( NODE_METHOD, (i.get_class_name(), "start", i.get_descriptor())) # link from start to run self.G.add_edge(n2.id, n1.id) #n2.add_edge( n1, {} ) # link from init to start for init in vm.get_method("<init>"): if init.get_class_name() == c.get_name(): #TODO: Leaving _get_existed_node to check if all the nodes are included #It is possible that internal_packages does not contain this node. Leaving _get_existed_node to check this n3 = self._get_node( NODE_CONSTRUCTOR, (init.get_class_name(), "<init>", init.get_descriptor())) self.G.add_edge(n3.id, n2.id)
def perform_analysis(inputApkPath, resultsDirPath, sourceFilesDirPath): logger.debug("Starting analysis of the application [%s]..." % inputApkPath) startTime = time.time() if not copyFileToDir(inputApkPath, sourceFilesDirPath): logger.error( "Could not copy source file to directory! The analysis was not performed!" ) return apkFileNameExt = os.path.basename(inputApkPath) apkFileName, _ = os.path.splitext(apkFileNameExt) apkFilePath = os.path.join(sourceFilesDirPath, apkFileNameExt) stadynaAnalyser = StadynaAnalyser() stadynaAnalyser.makeInitialAnalysis(apkFilePath) initial_name = apkFileName + "_initial" stadynaAnalyser.saveGexf(resultsDirPath, initial_name) if not stadynaAnalyser.containMethodsToAnalyse(): logger.info("Input apk file does not contain suspicious methods!") stadynaAnalyser.performInfoSave(resultsDirPath, apkFileName) logger.info("The analysis is finished!") return stadynaAnalyser.printMoiLists() dev = getDeviceForDynAnalysis() if not dev.is_alive(): logger.warning( "The selected device to perform dynamic analysis is not alive! Finishing!" ) stadynaAnalyser.performInfoSave(resultsDirPath, apkFileName) logger.info("The analysis is finished!") return #TODO: Check if it is possible racing conditions here #If we at first install application and then run Message analyser #everything is fine. #If we at first start Message analyser and then start the application #the first dex load is actual load when the application is installed. androApk = apk.APK(inputApkPath) installed = dev.install_package(inputApkPath) if not installed: logger.error( "An error occurred during the installation of the app [%s]! Cannot perform an analysis!" % inputApkPath) return package = androApk.get_package() mainActivity = androApk.get_main_activity() uid = dev.get_package_uid(package) if uid == -1: logger.error( "Cannot get the uid of the package [%s]! Cannot start an analysis!" % package) return #TODO: test section messages = Queue.Queue() seccon_producer = SecconMessageProducer(dev, messages) seccon_producer.setDaemon(False) seccon_producer.start() #sleeping 3sec before starting new activity time.sleep(3) #Add here the invocation of the main activity startMainActivity(dev, package, mainActivity) while 1: while not messages.empty(): line = messages.get() #print 'Received line: ' + repr(line) decodedLine = json.loads(line) if int(decodedLine.get(consts.JSON_UID)) != uid: continue analyseStadynaMsg(dev, sourceFilesDirPath, stadynaAnalyser, decodedLine) stadynaAnalyser.printMoiLists() try: time.sleep(2) #The same method can be used for different calls. Thus, we comment this line now ) #checkExit(stadynaAnalyser) except KeyboardInterrupt: logger.debug("Exiting...") break seccon_producer.stopThread() seccon_producer.join() endTime = time.time() stadynaAnalyser.performFinalInfoSave(resultsDirPath, apkFileName, (endTime - startTime)) logger.info("The analysis is finished!")
def perform_analysis(inputApkPath, resultsDirPath, sourceFilesDirPath): logger.debug("Starting analysis of the application [%s]..." % inputApkPath) startTime = time.time() if not copyFileToDir(inputApkPath, sourceFilesDirPath): logger.error("Could not copy source file to directory! The analysis was not performed!") return apkFileNameExt = os.path.basename(inputApkPath) apkFileName, _ = os.path.splitext(apkFileNameExt) apkFilePath = os.path.join(sourceFilesDirPath, apkFileNameExt) stadynaAnalyser = StadynaAnalyser() stadynaAnalyser.makeInitialAnalysis(apkFilePath) initial_name = apkFileName + "_initial" stadynaAnalyser.saveGexf(resultsDirPath, initial_name) if not stadynaAnalyser.containMethodsToAnalyse(): logger.info("Input apk file does not contain suspicious methods!") stadynaAnalyser.performInfoSave(resultsDirPath, apkFileName) logger.info("The analysis is finished!") return stadynaAnalyser.printMoiLists() dev = getDeviceForDynAnalysis() if not dev.is_alive(): logger.warning("The selected device to perform dynamic analysis is not alive! Finishing!") stadynaAnalyser.performInfoSave(resultsDirPath, apkFileName) logger.info("The analysis is finished!") return #TODO: Check if it is possible racing conditions here #If we at first install application and then run Message analyser #everything is fine. #If we at first start Message analyser and then start the application #the first dex load is actual load when the application is installed. androApk = apk.APK(inputApkPath) installed = dev.install_package(inputApkPath) if not installed: logger.error("An error occurred during the installation of the app [%s]! Cannot perform an analysis!" % inputApkPath) return package = androApk.get_package() mainActivity = androApk.get_main_activity() uid = dev.get_package_uid(package) if uid == -1: logger.error("Cannot get the uid of the package [%s]! Cannot start an analysis!" % package) return #TODO: test section messages = Queue.Queue() seccon_producer = SecconMessageProducer(dev, messages) seccon_producer.setDaemon(False) seccon_producer.start() #sleeping 3sec before starting new activity time.sleep(3) #Add here the invocation of the main activity startMainActivity(dev, package, mainActivity) while 1: while not messages.empty(): line = messages.get() #print 'Received line: ' + repr(line) decodedLine = json.loads(line) if int(decodedLine.get(consts.JSON_UID)) != uid: continue analyseStadynaMsg(dev, sourceFilesDirPath, stadynaAnalyser, decodedLine) stadynaAnalyser.printMoiLists() try: time.sleep(2) #The same method can be used for different calls. Thus, we comment this line now ) #checkExit(stadynaAnalyser) except KeyboardInterrupt: logger.debug("Exiting...") break seccon_producer.stopThread() seccon_producer.join() endTime = time.time() stadynaAnalyser.performFinalInfoSave(resultsDirPath, apkFileName, (endTime-startTime)) logger.info("The analysis is finished!")
def analyseFile(self, vmx, apk): vm = vmx.get_vm() self.androGuardObjects.append((apk, vm, vmx)) # self.internal_methods.extend(vm.get_methods()) #creating real internal nodes internal_called_methods = vmx.get_tainted_packages().stadyna_get_internal_called_methods() for method in internal_called_methods: class_name, method_name, descriptor = method nodeType = None if method_name == "<clinit>": nodeType = NODE_STATIC_INIT elif method_name == "<init>": nodeType = NODE_CONSTRUCTOR else: nodeType = NODE_METHOD n = self._get_node(nodeType, (class_name, method_name, descriptor)) n.set_attribute(ATTR_CLASS_NAME, class_name) n.set_attribute(ATTR_METHOD_NAME, method_name) n.set_attribute(ATTR_DESCRIPTOR, descriptor) self.G.add_node(n.id) #creating real edges (nodes are already there) #currently we are working only with internal packages. for j in vmx.get_tainted_packages().get_internal_packages(): src_class_name, src_method_name, src_descriptor = j.get_src(vm.get_class_manager()) dst_class_name, dst_method_name, dst_descriptor = j.get_dst(vm.get_class_manager()) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) # n1.set_attribute(ATTR_CLASS_NAME, src_class_name) # n1.set_attribute(ATTR_METHOD_NAME, src_method_name) # n1.set_attribute(ATTR_DESCRIPTOR, src_descriptor) n2 = self._get_existed_node((dst_class_name, dst_method_name, dst_descriptor)) # n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) # n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) # n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) self.G.add_edge(n1.id, n2.id) #adding fake class nodes for method in internal_called_methods: src_class_name, src_method_name, src_descriptor = method if src_method_name == "<init>" or src_method_name == "<clinit>": n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) n2 = self._get_node(NODE_FAKE_CLASS, src_class_name, None, False) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) if src_method_name == "<clinit>": self.G.add_edge(n1.id, n2.id) elif src_method_name == "<init>": self.G.add_edge(n2.id, n1.id) #real (external) reflection invoke nodes reflection_invoke_paths = analysis.seccon_get_invoke_method_paths(vmx) for j in reflection_invoke_paths: src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager() ) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager() ) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning("Cannot find the node [%s], where reflection invoke is called!" % (src_class_name, src_method_name, src_descriptor)) continue key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_REFL_INVOKE) n2 = self._get_node(NODE_REFL_INVOKE, key, LABEL_REFL_INVOKE, True) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) n2.set_attribute(ATTR_METHOD_NAME, src_method_name) n2.set_attribute(ATTR_DESCRIPTOR, src_descriptor) self.G.add_edge( n1.id, n2.id ) #real (external) reflection new instance nodes reflection_newInstance_paths = analysis.seccon_get_newInstance_method_paths(vmx) for j in reflection_newInstance_paths: src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager() ) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager() ) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning("Cannot find the node [%s], where reflection new instance is called!" % (src_class_name, src_method_name, src_descriptor)) continue key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_REFL_NEWINSTANCE) n2 = self._get_node(NODE_REFL_NEWINSTANCE, key, LABEL_REFL_NEWINSTANCE, True) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) n2.set_attribute(ATTR_METHOD_NAME, src_method_name) n2.set_attribute(ATTR_DESCRIPTOR, src_descriptor) self.G.add_edge( n1.id, n2.id ) #adding fake entry points if apk != None: for i in apk.get_activities() : j = bytecode.FormatClassToJava(i) n1 = self._get_existed_node((j, "onCreate", "(Landroid/os/Bundle;)V")) if n1 != None: key = "%s %s %s %s" % (j, "onCreate", "(Landroid/os/Bundle;)V", POSTFIX_ACTIVITY) n2 = self._get_node(NODE_FAKE_ACTIVITY, key, LABEL_ACTIVITY, False) self.G.add_edge( n2.id, n1.id ) self.entry_nodes.append( n1.id ) for i in apk.get_services() : j = bytecode.FormatClassToJava(i) n1 = self._get_existed_node( (j, "onCreate", "()V") ) if n1 != None : key = "%s %s %s %s" % (j, "onCreate", "()V", POSTFIX_SERVICE) n2 = self._get_node(NODE_FAKE_SERVICE, key, LABEL_SERVICE, False) self.G.add_edge( n2.id, n1.id ) self.entry_nodes.append( n1.id ) for i in apk.get_receivers() : j = bytecode.FormatClassToJava(i) n1 = self._get_existed_node( (j, "onReceive", "(Landroid/content/Context;Landroid/content/Intent;)V") ) if n1 != None : key = "%s %s %s %s" % (j, "onReceive", "(Landroid/content/Context;Landroid/content/Intent;)V", POSTFIX_RECEIVER) n2 = self._get_node(NODE_FAKE_SERVICE, key, LABEL_RECEIVER, False) self.G.add_edge( n2.id, n1.id ) self.entry_nodes.append( n1.id ) #fake permissions list_permissions = vmx.stadyna_get_permissions([]) for x in list_permissions: for j in list_permissions[x]: if isinstance(j, PathVar): continue src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager() ) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager() ) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning("Cannot find node [%s %s %s] for permission [%s]!" % (src_class_name, src_method_name, src_descriptor, x)) continue #SOURCE, DEST, POSTFIX, PERMISSION_NAME key = "%s %s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_PERM, x) n2 = self._get_node(NODE_FAKE_PERMISSION, key, x, False) n2.set_attribute(ATTR_CLASS_NAME, dst_class_name) n2.set_attribute(ATTR_METHOD_NAME, dst_method_name) n2.set_attribute(ATTR_DESCRIPTOR, dst_descriptor) n2.set_attribute(ATTR_PERM_NAME, x) n2.set_attribute(ATTR_PERM_LEVEL, MANIFEST_PERMISSIONS[ x ][0]) self.G.add_edge(n1.id, n2.id) #fake DexClassLoader nodes dyn_code_loading = analysis.seccon_get_dyncode_loading_paths(vmx) for j in dyn_code_loading: src_class_name, src_method_name, src_descriptor = j.get_src( vm.get_class_manager() ) dst_class_name, dst_method_name, dst_descriptor = j.get_dst( vm.get_class_manager() ) n1 = self._get_existed_node((src_class_name, src_method_name, src_descriptor)) if n1 == None: logger.warning("Cannot find dexload node [%s]!" % (src_class_name, src_method_name, src_descriptor)) continue key = "%s %s %s %s %s %s %s" % (src_class_name, src_method_name, src_descriptor, dst_class_name, dst_method_name, dst_descriptor, POSTFIX_DEXLOAD) n2 = self._get_node(NODE_FAKE_DEXLOAD, key, LABEL_DEXLOAD, False) n2.set_attribute(ATTR_CLASS_NAME, src_class_name) n2.set_attribute(ATTR_METHOD_NAME, src_method_name) n2.set_attribute(ATTR_DESCRIPTOR, src_descriptor) self.G.add_edge( n1.id, n2.id ) # Specific Java/Android library for c in vm.get_classes(): #if c.get_superclassname() == "Landroid/app/Service;" : # n1 = self._get_node( c.get_name(), "<init>", "()V" ) # n2 = self._get_node( c.get_name(), "onCreate", "()V" ) # self.G.add_edge( n1.id, n2.id ) if c.get_superclassname() == "Ljava/lang/Thread;" or c.get_superclassname() == "Ljava/util/TimerTask;" : for i in vm.get_method("run") : if i.get_class_name() == c.get_name() : n1 = self._get_node(NODE_METHOD, (i.get_class_name(), i.get_name(), i.get_descriptor())) n2 = self._get_node(NODE_METHOD, (i.get_class_name(), "start", i.get_descriptor())) # link from start to run self.G.add_edge( n2.id, n1.id ) #n2.add_edge( n1, {} ) # link from init to start for init in vm.get_method("<init>") : if init.get_class_name() == c.get_name(): #TODO: Leaving _get_existed_node to check if all the nodes are included #It is possible that internal_packages does not contain this node. Leaving _get_existed_node to check this n3 = self._get_node(NODE_CONSTRUCTOR, (init.get_class_name(), "<init>", init.get_descriptor())) self.G.add_edge( n3.id, n2.id )