def each(self, target): self.results = dict() try: apk, vm, vm_analysis = AnalyzeAPK(target) # First, get basic information about the APK self.results['name'] = apk.get_app_name() self.results['package'] = apk.get_package() self.results['permissions'] = apk.get_permissions() self.results['main_activity'] = apk.get_main_activity() self.results['receivers'] = apk.get_receivers() self.results['services'] = apk.get_services() self.results['main_activity_content'] = vm.get_class("L{};".format( self.results['main_activity']).replace('.', '/')).get_source() except: apk = None vm, vm_analysis = AnalyzeDex(target) self.results['dex'] = True # Then, run all the APK Plugins in order to see if this is a known malware for plugin in APKPlugin.__subclasses__(): plugin = plugin(target, apk, vm, vm_analysis) plugin.apply(self) return True
class Apkinfo: def __init__(self, apk_path): print(f"Init {apk_path}") self.apk, self.dalvikvmformat, self.analysis = AnalyzeAPK(apk_path) # get package name and the main launch activity to start the app. self._packageName = self.apk.get_package() self._mainActivity = self.apk.get_main_activity() @property def package_name(self): return self._packageName @property def main_activity(self): return self._mainActivity def get_android_api(self): """ Returns a list of Android APIs call. :return: (class_name, method_name) """ method_analysis = self.analysis.get_android_api_usage() for meth in method_analysis: yield meth.class_name, meth.name def launch_activity(self): return f"{self._packageName}/{self._mainActivity}"
def each(self, target): self.results = dict(name=None, files=[], package=None, permissions=[], declared_permissions=[], main_activity=None, activities=[], receivers=[], services=[], manifest=None, libraries=[], main_activity_content=None, internal_classes=[]) try: apk, vm, vm_analysis = AnalyzeAPK(target) # First, get basic information about the APK self.results['name'] = apk.get_app_name() self.results['files'] = apk.get_files_types() self.results['package'] = apk.get_package() self.results['permissions'] = apk.get_details_permissions() self.results[ 'declared_permissions'] = apk.get_declared_permissions_details( ) self.results['main_activity'] = apk.get_main_activity() self.results['activities'] = apk.get_activities() self.results['receivers'] = apk.get_receivers() self.results['services'] = apk.get_services() self.results['manifest'] = apk.get_android_manifest_axml().get_xml( ) self.results['libraries'] = list(apk.get_libraries()) self.results['main_activity_content'] = None self.results['internal_classes'] = [] try: self.results['main_activity_content'] = self.results[ 'main_activity_content'] = vm[0].get_class( "L{};".format(self.results['main_activity']).replace( '.', '/')).get_source() except: self.log('error', traceback.print_exc()) try: self.results['internal_classes'] = self._get_internal_classes( vm_analysis) self._store_internal_classes() except: self.log('error', traceback.print_exc()) # Then, run all the APK Plugins in order to see if this is a known malware for plugin in APKPlugin.__subclasses__(): plugin = plugin(target, apk, vm, vm_analysis) plugin.apply(self) except: self.log('error', traceback.print_exc()) return True
#!/usr/bin/python2 from collections import defaultdict from androguard.misc import AnalyzeAPK apk_path = raw_input('Provide path to apk\n') print('Analyzing...') apk, dex, vm = AnalyzeAPK(apk_path) print('\nActivities:') for activity in apk.get_activities(): print(activity) print('\nMain activity:') print(apk.get_main_activity()) print('\nSuper dangerous permissions:') for perm, details in apk.get_details_permissions().items(): if details[0] == 'dangerous': # Protection levels: https://developer.android.com/guide/topics/permissions/overview#normal-dangerous print(perm) print('\nServices:') for service in apk.get_services(): print(service) print('\nBroadcasts:') for receiver in apk.get_receivers(): print(receiver)
'min_sdk_version': int(apk.get_min_sdk_version()), 'version_code': apk.xml['AndroidManifest.xml'].get( '{http://schemas.android.com/apk/res/android}versionCode'), 'libraries': list(apk.get_libraries()), 'androidtv': apk.is_androidtv(), 'target_sdk_version': apk.get_target_sdk_version(), 'api_keys': {}, # TODO 'activities': apk.get_activities(), 'main_activity': apk.get_main_activity(), 'receivers': apk.get_receivers(), 'signature_name': apk.get_signature_name(), 'dexes': {}, 'displayed_version': apk.xml['AndroidManifest.xml'].get( '{http://schemas.android.com/apk/res/android}versionName'), 'services': apk.get_services(), 'permissions': apk.get_permissions(), 'cordova': None, #What is this ? 'functionalities': {},
class AndroidPackage(object): """Static android information.""" def __init__(self, filepath): self.filepath = filepath self.apk = None self.analysis = None def _get_detailed_permissions(self): """Return a list of all permission requests by the application.""" perms = [] for k, v in self.apk.get_details_permissions().items(): perms.append({ "name": k, "protection_level": v[0], "description": v[2] }) return perms def _enumerate_services(self): """Return a list of all services with their actions""" services = [] for _service in self.apk.get_services(): service = {} service["name"] = _service service["action"] = [] intent_filters = self.apk.get_intent_filters("service", _service) if "action" in intent_filters: service["action"] = intent_filters["action"] services.append(service) return services def _enumerate_receivers(self): """Return a list of all BroadcastReceiver's with their actions""" receivers = [] for _receiver in self.apk.get_receivers(): receiver = {} receiver["name"] = _receiver receiver["action"] = [] intent_filters = self.apk.get_intent_filters("receiver", _receiver) if "action" in intent_filters: receiver["action"] = intent_filters["action"] receivers.append(receiver) return receivers def _enumerate_apk_files(self): """Return a list of files in the APK.""" files = [] for filename, filetype in self.apk.get_files_types().items(): buf = self.apk.zip.read(filename) files.append({ "name": filename, "md5": hashlib.md5(buf).hexdigest(), "size": len(buf), "type": filetype, }) return files def _enumerate_encrypted_assets(self): """Returns a list of files in the APK assets that have high entropy.""" files = [] for filename, filetype in self.apk.get_files_types().items(): if "assets" in filename: buf = self.apk.zip.read(filename) file_entropy = entropy.shannon_entropy(buf) if file_entropy > 0.9: files.append({ "name": filename, "entropy": file_entropy, "size": len(buf), "type": filetype, }) return files def _get_certificates_info(self): """Return a list of APK certificates""" certficates = [] for cert in self.apk.get_certificates(): not_valid_after = cert['tbs_certificate']['validity'][ 'not_after'].native not_valid_before = cert['tbs_certificate']['validity'][ 'not_before'].native certficates.append({ "sha1": cert.sha1.encode("hex"), "sha256": cert.sha256.encode("hex"), "issuer": cert.issuer.human_friendly, "subject": cert.subject.human_friendly, "not_valid_after": not_valid_after.strftime("%Y-%m-%d %H:%M:%S"), "not_valid_before": not_valid_before.strftime("%Y-%m-%d %H:%M:%S"), "public_key_algorithm": cert.public_key.algorithm, "public_key_size": "%d bit" % cert.public_key.bit_size, "signature_algorithm": cert.signature_algo + " with " + cert.hash_algo, "signature": cert.signature.encode("hex"), "serial_number": str(cert.serial_number) }) return certficates def _enumerate_native_methods(self): """Return a list of all methods compiled in the application""" methods = [] for mca in self.analysis.get_methods(): if mca.is_external(): continue if not mca.get_method().get_access_flags() & 0x0100: continue methods.append(self._get_pretty_method(mca)) return methods def _get_pretty_method(self, mca): """Return a string representation of an API method. @param mca: MethodClassAnalysis object. """ class_name = mca.get_method().get_class_name().replace("/", ".")[1:-1] method_name = mca.get_method().get_name() return "%s.%s%s" % (class_name, method_name, mca.descriptor) def _enumerate_api_calls(self): """Return a dictionary of all APIs with their xrefs.""" classes = [] exclude_pattern = re.compile( "^(Lcom/google/|Landroid|Ljava|Lcom/sun/|Lorg/apache/|" "Lorg/spongycastle|Lmyjava/|Lkotlin/)") for ca in self.analysis.get_classes(): if ca.is_external(): continue if exclude_pattern.match(ca.name): continue classes.append(ca.name) calls = [] for class_name in classes: for mca in self.analysis.find_methods(class_name): xrefs_to = [] for _, m, _ in mca.get_xref_to(): callee_class = m.get_class_name().replace("/", ".")[1:-1] callee_api = "%s.%s" % (callee_class, m.get_name()) xrefs_to.append(callee_api) if not xrefs_to: continue api = {} api["name"] = self._get_pretty_method(mca) api["callees"] = xrefs_to calls.append(api) return calls def run(self): """Run androguard to extract static APK information @return: dict of static features. """ from androguard.misc import AnalyzeAPK logging.getLogger("androguard.dvm").setLevel(logging.WARNING) logging.getLogger("androguard.analysis").setLevel(logging.WARNING) logging.getLogger("androguard.misc").setLevel(logging.WARNING) logging.getLogger("androguard.apk").setLevel(logging.CRITICAL) try: self.apk, _, self.analysis = AnalyzeAPK(self.filepath) except (OSError, zipfile.BadZipfile) as e: log.error("Error parsing APK file: %s", e) return None manifest = {} if self.apk.is_valid_APK(): manifest["package"] = self.apk.get_package() manifest["services"] = self._enumerate_services() manifest["receivers"] = self._enumerate_receivers() manifest["providers"] = self.apk.get_providers() manifest["activities"] = self.apk.get_activities() manifest["main_activity"] = self.apk.get_main_activity() manifest["permissions"] = self._get_detailed_permissions() apkinfo = {} apkinfo["manifest"] = manifest apkinfo["files"] = self._enumerate_apk_files() apkinfo["encrypted_assets"] = self._enumerate_encrypted_assets() apkinfo["is_signed_v1"] = self.apk.is_signed_v1() apkinfo["is_signed_v2"] = self.apk.is_signed_v2() apkinfo["certificates"] = self._get_certificates_info() apkinfo["native_methods"] = self._enumerate_native_methods() apkinfo["api_calls"] = self._enumerate_api_calls() return apkinfo
def run(self): app.logger.info('new analysis') s = Session() self.status = 'Analyzing APK' a, d, dx = AnalyzeAPK(self.target_file, session=s) #APK,list[DalvikVMFormat],Analysis print(type(a), type(d[0]), type(dx)) #cache activities, receivers, services, and providers, because for some reason, saving the Session causes a bug, breaking getters """i.e. bytecodes/apk.py", line 582, in get_elements for item in self.xml[i].findall('.//' + tag_name): TypeError: string indices must be integers """ activities = a.get_activities() receivers = a.get_receivers() services = a.get_services() providers = a.get_providers() self.main_activity = a.get_main_activity() if self.session_save_file: sys.setrecursionlimit(100000000) self.status = 'Saving session file' Save(s, self.session_save_file) cached_analyses.append({'md5': self.md5, 'analysis': (a, d, dx)}) #gather all classes from dexs 'd' #classes = get_all_classes_from_dexs(d) classes = dx.classes total_num = len(classes) done = 0 #num of done classes #result_classes contains the completed analysis info for each class run through the ClassAnalysis object result_classes = [] analysis_start_time = time.time() self.status = 'Getting all classes' for c_name, c_analysis in classes.items(): ca = ClassAnalysis(c_name, c_analysis, activities, receivers, services, providers) ca_result = ca.run() result_classes.append(ca_result) done += 1 if done % ceil(total_num / 100) == 0: self.progress += 1 #app.logger.info(self.progress) # with app.test_request_context('/'): # socketio.emit('newstatus', {'data':self.progress}, namespace='/status') analysis_end_time = time.time() analysis_total_time = analysis_end_time - analysis_start_time #debugging: self.status = 'Writing beforenetworkx debugging JSON' with open(self.graph_out_path + '.beforenetworkx', 'w') as f: json.dump(result_classes, f, indent=4, separators=(',', ': '), sort_keys=True) #create a networkx graph given the completed analyses in result_classess create_graph_start_time = time.time() self.status = 'Creating graph out of {} classes analyzed'.format( len(result_classes)) graph = create_graph(classes=result_classes) create_graph_end_time = time.time() create_graph_total_time = create_graph_end_time - create_graph_start_time #write graph to file: graph_out_path write_graph_start_time = time.time() self.status = 'Writing graph to disk' write_graph(graph, self.graph_out_path) write_graph_end_time = time.time() write_graph_total_time = write_graph_end_time - write_graph_start_time #build and write another graph that contains only providers,receivers,activities, and services if self.component_subgraph_out_path: component_names = [] self.status = 'Getting component nodes from graph' for node in graph: node_tmp = graph.node[node] if node_tmp[ 'component_type'] != NonComponentType.EXTERNAL and node_tmp[ 'component_type'] != NonComponentType.INTERNAL: component_names.append(node_tmp['name']) self.status = 'Creating subgraph containing only components' subgraph = get_class_subgraph(graph, class_names=component_names) self.status = 'Writing subgraph to disk' write_graph(subgraph, self.component_subgraph_out_path) #app metadata for misc/debugging apk_size = os.path.getsize(self.target_file) self.status = 'Writing metadata' self.write_app_metadata(result_classes, a, analysis_total_time, apk_size, create_graph_total_time, write_graph_total_time) #debugging # with open(self.graph_out_path+'.runmetrics', 'w') as f: # json.dump() self.progress = 100 self.status = 'Done' self.paused.wait( ) #wait for caller to collect last status and reset event before finishing app.logger.info('done')
outDexName = "EXTRACTED_DEX_FROM_" + tail print() print("======== KEY AND PACKAGE INFO ========") outDexContent, locationDEX = DEXDecrypt.decrypt(a, d, dx, sys.argv[1]) with open(outDexName, "wb") as file: file.write(outDexContent) file.close() #Search for the class with configuration parameters cfg_src = source.getStringsClassSource(a, d, dx, outDexName) #Decrypt the strings, currently returns C2 URL print() print("======== DECRYPTED STRINGS ========") decrypted_cfg_src = decryptConfig.all(cfg_src) #Collect and print other IoC's appName = a.get_app_name() mainActivity = a.get_main_activity() minSDK = a.get_min_sdk_version() sha1apk = FileHash('sha1').hash_file(sys.argv[1]) C2URL = decrypted_cfg_src fileSize = os.path.getsize(sys.argv[1]) * (10**-6) print() print("======== IOC INFORMATION ========") print("AppName, MainActivity, MINSDK, EncryptedDEX, SHA1Sum, C&C ,Size(MB)") print(appName, ",", mainActivity, ",", minSDK, ",", locationDEX, ",", sha1apk, ",", C2URL, ",", fileSize)