def GetFromInstructions(ApkDirectoryPath, ApkFile, PMap, RequestedPermissionList): ''' Get required permissions, used Apis and HTTP information for an ApkFile. Reloaded version of GetPermissions. :param String ApkDirectoryPath :param String ApkFile :param PScoutMapping.PScoutMapping PMap :param RequestedPermissionList List([String]) :return UsedPermissions :rtype Set([String]) :return RestrictedApiSet :rtype Set([String]) :return SuspiciousApiSet :rtype Set([String]) :return URLDomainSet :rtype Set([String]) ''' UsedPermissions = set() RestrictedApiSet = set() SuspiciousApiSet = set() URLDomainSet = set() try: ApkFile = os.path.abspath(ApkFile) a, d, dx = androlyze.AnalyzeAPK(ApkFile) except Exception as e: print e logger.error(e) ProcessingResultslogger.error("Executing Androlyze on " + ApkFile + " Failed.") return for method in d.get_methods(): g = dx.get_method(method) for BasicBlock in g.get_basic_blocks().get(): Instructions = BasicBlockAttrBuilder.GetBasicBlockDalvikCode( BasicBlock) Apis, SuspiciousApis = BasicBlockAttrBuilder.GetInvokedAndroidApis( Instructions) Permissions, RestrictedApis = BasicBlockAttrBuilder.GetPermissionsAndApis( Apis, PMap, RequestedPermissionList) UsedPermissions = UsedPermissions.union(Permissions) RestrictedApiSet = RestrictedApiSet.union(RestrictedApis) SuspiciousApiSet = SuspiciousApiSet.union(SuspiciousApis) for Instruction in Instructions: URLSearch = re.search( "https?://([\da-z\.-]+\.[a-z\.]{2, 6}|[\d.]+)[^'\"]*", Instruction, re.IGNORECASE) if (URLSearch): URL = URLSearch.group() Domain = re.sub( "https?://(.*)", "\g<1>", re.search("https?://([^/:\\\\]*)", URL, re.IGNORECASE).group(), 0, re.IGNORECASE) URLDomainSet.add(Domain) # Got Set S6, S5, S7, S8 described in Drebian paper return UsedPermissions, RestrictedApiSet, SuspiciousApiSet, URLDomainSet
def GetCFGString(ApkFName, OutputFName): ''' Get the CFG string of an apk and write to a txt file :param String ApkFName: absolute path of the apk file :param String OutputFName: absolute path of the txt file ''' if os.path.isfile(OutputFName): print 'Output file: {} exists, so skipping {}'.format( OutputFName, ApkFName) return T0 = time() try: a, d, dx = androlyze.AnalyzeAPK(ApkFName) except zipfile.BadZipfile: # if file is not an APK, may be a dex object try: d, dx = androlyze.AnalyzeDex(ApkFName) except Exception as e: # if file cannot be processed as an apk nor a dex object # it may be malformed file, then just return return except Exception as e: return OutputFH = open(OutputFName, 'w+') try: for method in d.get_methods(): SignatureText = dx.get_method_signature( method, predef_sign=androlyze.analysis.SIGNATURE_L0_0).get_string() SignatureText = string.replace(SignatureText, 'B[]', 'B[NULL]') ProcessedText = re.sub(r"B\[(.*?)\]", "\g<1> ", SignatureText) if ProcessedText: print >> OutputFH, ProcessedText finally: OutputFH.close() print 'Done generating CFG signature strings for file: {} in {} sec.'.format( os.path.basename(ApkFName), round(time() - T0, 2))
def performAnalysis(apkPath): # Perform the analysis by recalling public method of androlyze.py a, d, dx = androlyze.AnalyzeAPK(apkPath) if not a.is_valid_APK(): print "[Exit] The selected resource is not a valid APK!" return sys.exit(FAILURE) if not analysis.is_dyn_code(dx): print "[Exit] No DexClassLoader use in this APK and so there is nothing to patch!" return sys.exit(SUCCESS) print "[In progress] Analyze target APK.." # Store app permissions (used later on while patching Android Manifest) app_permissions = set(a.get_permissions()) #app_permissions.add('android.permission.ACCESS_NETWORK_STATE') #app_permissions.add('android.permission.BLABLABLA') # Reference to global variable missing_permissions = list() for current_perm in required_permissions: if not (current_perm in app_permissions): missing_permissions.append(current_perm) #print missing_permissions # Flag variable for later use dynamicCallsWhereTraced = False # Save a reference for standard output stdout = sys.stdout # Redirect output to an helper variable # sys.stdout = open('./dynamicCalls', 'w') dynamicCallsFilePath = os.curdir + os.sep + "dynamicCalls" with open(dynamicCallsFilePath, 'w') as dynamicCallsFile: # Redirect output to an helper variable sys.stdout = dynamicCallsFile # Highlight dynamic calls linked to a DexClassLoader. analysis.show_DynCode(dx) # Set back usual stdout sys.stdout = stdout with open(dynamicCallsFilePath, 'r') as dynamicCallsFile: #print INTEGER.parseString("1") #print CLASS_STRING.parseString("Lcom/example/extractapp/MainActivity;") #print METHOD_DECL.parseString("setUpNormal()V") #print NUMBER_EXA.parseString("(0x66)") #print CLASS_STRING.parseString("Ldalvik/system/DexClassLoader;") # Define a grammar to parse line of the input file. parsing_format = INTEGER + CLASS_STRING + "->" + METHOD_DECL + NUMBER_EXA + "--->" + CLASS_STRING + "->" + METHOD_DECL # Reference to global variable classesWithDynCodeLoad = list() for line in dynamicCallsFile: # print line tokens = parsing_format.parseString(line) # This is a sort specific parsing constant(magic numbers).. className = tokens[1][1] # Extract only name of classes to patch which are not the ones of Grab'n Run if className != "it/necst/grabnrun/SecureDexClassLoader": classesWithDynCodeLoad.append(className) # In the end remove duplicates from this list (use a set) classesWithDynCodeLoad = set(classesWithDynCodeLoad) # print classesWithDynCodeLoad # Raise flag variable print "[In progress] Dynamic calls have been detected.." dynamicCallsWhereTraced = True # Now the helper file should be closed and erased. os.remove(dynamicCallsFilePath) if dynamicCallsWhereTraced: # This APK should be patched! return missing_permissions, classesWithDynCodeLoad # Something went wrong.. print "[Exit] Dynamic calls have not been detected!" return sys.exit(FAILURE)
def analyze(path_to_apk, security): apk_zip = zipfile.ZipFile(path_to_apk) apk_size = os.path.getsize(path_to_apk) dex_size = apk_zip.getinfo('classes.dex').file_size # Analyze apk a, d, dx = androlyze.AnalyzeAPK(path_to_apk, decompiler='dad') # APK features if a.get_min_sdk_version() is not None: min_andrversion = str(a.get_min_sdk_version()) else: min_andrversion = 0 if a.get_max_sdk_version() is not None: max_andrversion = str(a.get_max_sdk_version()) else: max_andrversion = 0 if a.get_target_sdk_version() is not None: target_andrversion = str(a.get_target_sdk_version()) else: target_andrversion = 0 # if a.get_androidversion_name() is not None: # versionName = str(a.get_androidversion_name()) # else: # versionName = '' methodCount = d.get_len_methods() classCount = len(d.get_classes()) crypto_count = len(dx.get_tainted_packages().search_methods( 'Ljava/crypto/.', '.', '.')) dynCode_count = len(dx.get_tainted_packages().search_methods( 'Ldalvik/system/DexClassLoader/.', '.', '.')) native_count = len(dx.get_tainted_packages().search_methods( 'Ljava/lang/System;', '.', '.')) reflect_count = len(dx.get_tainted_packages().search_methods( 'Ljava/lang/reflect/Method;', '.', '.')) fileCount = len(a.get_files()) # API features # sendSMS if (len(dx.get_tainted_packages().search_methods( 'Landroid/telephony/SmsManager;', 'send[a-zA-Z]+Message', '.')) > 0) or (len(dx.get_tainted_packages().search_methods( 'Landroid/telephony/gsm/SmsManager;', 'send[a-zA-Z]+Message', '.')) > 0): sendSMS = 1 else: sendSMS = 0 # deleteSMS, previously all zero if len(dx.get_tainted_packages().search_methods( 'Landroid/content/ContentResolver;', 'delete', '')) > 0: deleteSMS = 1 else: deleteSMS = 0 # interruptSMS if len(dx.get_tainted_packages().search_methods( 'Landroid/content/BroadcastReceiver;', 'abortBroadcast', '.')) > 0: interruptSMS = 1 else: interruptSMS = 0 # httpPost if (len(dx.get_tainted_packages().search_methods( 'Lorg/apache/http/client/methods/HttpPost;', '.', '.')) > 0) or (len(dx.get_tainted_packages().search_methods( 'Ljava/net/HttpURLConnection;', '.', '.')) > 0): httpPost = 1 else: httpPost = 0 # deviceId if len(dx.get_tainted_packages().search_methods( 'Landroid/telephony/TelephonyManager;', 'getDeviceId', '.')) > 0: deviceId = 1 else: deviceId = 0 # simCountry if len(dx.get_tainted_packages().search_methods( 'Landroid/telephony/TelephonyManager;', 'getSimCountryIso', '.')) > 0: simCountry = 1 else: simCountry = 0 # installedPkg if len(dx.get_tainted_packages().search_methods( 'Landroid/content/pm/PackageManager;', 'getInstalledPackages', '.')) > 0: installedPkg = 1 else: installedPkg = 0 # loadOtherCode loadOtherCode = 0 # subprocess if (len(dx.get_tainted_packages().search_methods( 'Ljava/lang/ProcessBuilder;', 'start', '.')) > 0) or (len( dx.get_tainted_packages().search_methods( 'Ljava/lang/Runtime;', 'exec', '.')) > 0): subprocess = 1 else: subprocess = 0 # executeOtherCode executeOtherCode = 0 # jni if len(dx.get_tainted_packages().search_methods('Ljava/lang/System;', 'loadLibrary', '.')) > 0: jni = 1 else: jni = 0 # unix unix = 0 # Widget features fields = dx.get_tainted_fields() field_list = [] for field in fields: field_list.append(field) # widget count # EditViewCount, previously all zero # ToastCount, previously all zero buttonCount, TextViewCount, EditViewCount, ImageButtonCount, CheckBoxCount, RadioGroupCount, RadioButtonCount, ToastCount, SpinnerCount, ListViewCount = 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 for field in field_list: if 'Landroid/widget/Button;' in field[1]: buttonCount += 1 elif 'Landroid/widget/TextView;' in field[1]: TextViewCount += 1 elif 'Landroid/widget/EditText;' in field[1]: EditViewCount += 1 elif 'Landroid/widget/ImageButton;' in field[1]: ImageButtonCount += 1 elif 'Landroid/widget/CheckBox;' in field[1]: CheckBoxCount += 1 elif 'Landroid/widget/RadioGroup;' in field[1]: RadioGroupCount += 1 elif 'Landroid/widget/RadioButton;' in field[1]: RadioButtonCount += 1 elif 'Landroid/widget/Toast;' in field[1]: ToastCount += 1 elif 'Landroid/widget/Spinner;' in field[1]: SpinnerCount += 1 elif 'Landroid/widget/ListView;' in field[1]: ListViewCount += 1 # Permission features # INTERNET if 'android.permission.INTERNET' in a.get_permissions(): INTERNET = 1 else: INTERNET = 0 # SET_DEBUG_APP if 'android.permission.SET_DEBUG_APP' in a.get_permissions(): SET_DEBUG_APP = 1 else: SET_DEBUG_APP = 0 # MODIFY_PHONE_STATE if 'android.permission.MODIFY_PHONE_STATE' in a.get_permissions(): MODIFY_PHONE_STATE = 1 else: MODIFY_PHONE_STATE = 0 # RECORD_AUDIO if 'android.permission.RECORD_AUDIO' in a.get_permissions(): RECORD_AUDIO = 1 else: RECORD_AUDIO = 0 # RECEIVE_BOOT_COMPLETED if 'android.permission.RECEIVE_BOOT_COMPLETED' in a.get_permissions(): RECEIVE_BOOT_COMPLETED = 1 else: RECEIVE_BOOT_COMPLETED = 0 # RECEIVE_MMS if 'android.permission.RECEIVE_MMS' in a.get_permissions(): RECEIVE_MMS = 1 else: RECEIVE_MMS = 0 # RECEIVE_SMS if 'android.permission.RECEIVE_SMS' in a.get_permissions(): RECEIVE_SMS = 1 else: RECEIVE_SMS = 0 # RECEIVE_WAP_PUSH if 'android.permission.RECEIVE_WAP_PUSH' in a.get_permissions(): RECEIVE_WAP_PUSH = 1 else: RECEIVE_WAP_PUSH = 0 # SEND_SMS if 'android.permission.SEND_SMS' in a.get_permissions(): SEND_SMS = 1 else: SEND_SMS = 0 # CALL_PHONE if 'android.permission.CALL_PHONE' in a.get_permissions(): CALL_PHONE = 1 else: CALL_PHONE = 0 # CALL_PRIVILEGED if 'android.permission.CALL_PRIVILEGED' in a.get_permissions(): CALL_PRIVILEGED = 1 else: CALL_PRIVILEGED = 0 # PROCESS_OUTGOING_CALLS if 'android.permission.PROCESS_OUTGOING_CALLS' in a.get_permissions(): PROCESS_OUTGOING_CALLS = 1 else: PROCESS_OUTGOING_CALLS = 0 # READ_CALL_LOG if 'android.permission.READ_CALL_LOG' in a.get_permissions(): READ_CALL_LOG = 1 else: READ_CALL_LOG = 0 # READ_EXTERNAL_STORAGE if 'android.permission.READ_EXTERNAL_STORAGE' in a.get_permissions(): READ_EXTERNAL_STORAGE = 1 else: READ_EXTERNAL_STORAGE = 0 # READ_LOGS if 'android.permission.READ_LOGS' in a.get_permissions(): READ_LOGS = 1 else: READ_LOGS = 0 # ACCESS_COARSE_LOCATION if 'android.permission.ACCESS_COARSE_LOCATION' in a.get_permissions(): ACCESS_COARSE_LOCATION = 1 else: ACCESS_COARSE_LOCATION = 0 # ACCESS_FINE_LOCATION if 'android.permission.ACCESS_FINE_LOCATION' in a.get_permissions(): ACCESS_FINE_LOCATION = 1 else: ACCESS_FINE_LOCATION = 0 # BLUETOOTH if 'android.permission.BLUETOOTH' in a.get_permissions(): BLUETOOTH = 1 else: BLUETOOTH = 0 # CAMERA if 'android.permission.CAMERA' in a.get_permissions(): CAMERA = 1 else: CAMERA = 0 # INSTALL_PACKAGES if 'android.permission.INSTALL_PACKAGES' in a.get_permissions(): INSTALL_PACKAGES = 1 else: INSTALL_PACKAGES = 0 # NFC if 'android.permission.NFC' in a.get_permissions(): NFC = 1 else: NFC = 0 # READ_CONTACTS if 'android.permission.READ_CONTACTS' in a.get_permissions(): READ_CONTACTS = 1 else: READ_CONTACTS = 0 # Manifest features # # sharedUserId # if a.get_AndroidManifest().getElementsByTagName('manifest')[0].getAttribute('android:sharedUserId') is not None: # sharedUserId = a.get_AndroidManifest().getElementsByTagName('manifest')[0].getAttribute('android:sharedUserId') # else: # sharedUserId = '' # permissionCount, previously all zero permissionCount = len(a.get_permissions()) # activityCount activityCount = len(a.get_activities()) # serviceCount serviceCount = len(a.get_services()) # receiverCount receiverCount = len(a.get_receivers()) # providerCount providerCount = len(a.get_providers()) # exportedCount, previously all zero exportedCount = 0 for activity in a.get_AndroidManifest().getElementsByTagName('activity'): if activity.getAttribute('android:exported') == 'true': exportedCount += 1 for service in a.get_AndroidManifest().getElementsByTagName('service'): if activity.getAttribute('android:exported') == 'true': exportedCount += 1 for receiver in a.get_AndroidManifest().getElementsByTagName('receiver'): if activity.getAttribute('android:exported') == 'true': exportedCount += 1 for provider in a.get_AndroidManifest().getElementsByTagName('provider'): if activity.getAttribute('android:exported') == 'true': exportedCount += 1 # # backupAgent # if a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:backupAgent') is not None: # backupAgent = a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:backupAgent') # else: # backupAgent = '' # # killAfterRestore # if a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:killAfterRestore') is not None: # killAfterRestore = a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:killAfterRestore') # else: # killAfterRestore = '' # # allowTaskReparenting # if a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:allowTaskReparenting') is not None: # allowTaskReparenting = a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:allowTaskReparenting') # else: # allowTaskReparenting = '' # # process # if a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:process') is not None: # process = a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:process') # else: # process = '' # # taskAffinity # if a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:taskAffinity') is not None: # taskAffinity = a.get_AndroidManifest().getElementsByTagName('application')[0].getAttribute('android:taskAffinity') # else: # taskAffinity = '' # hPictureCount # mPictureCount # lPictureCount # xPictureCount, previously all zero hPictureCount, mPictureCount, lPictureCount, xPictureCount = 0, 0, 0, 0 for info in a.zip.infolist(): if 'res/drawable-hdpi' in info.filename: hPictureCount += 1 elif 'res/drawable-mdpi' in info.filename: mPictureCount += 1 elif 'res/drawable-ldpi' in info.filename: lPictureCount += 1 elif 'res/drawable-xhdpi' in info.filename: xPictureCount += 1 # totalCount totalCount = hPictureCount + mPictureCount + lPictureCount + xPictureCount # Initialize FeatureVector fv = FeatureVector( apk_size, dex_size, min_andrversion, max_andrversion, target_andrversion, security, methodCount, classCount, crypto_count, dynCode_count, native_count, reflect_count, sendSMS, deleteSMS, interruptSMS, httpPost, deviceId, simCountry, installedPkg, loadOtherCode, subprocess, executeOtherCode, jni, unix, buttonCount, TextViewCount, EditViewCount, ImageButtonCount, CheckBoxCount, RadioGroupCount, RadioButtonCount, ToastCount, SpinnerCount, ListViewCount, fileCount, INTERNET, SET_DEBUG_APP, MODIFY_PHONE_STATE, RECORD_AUDIO, RECEIVE_BOOT_COMPLETED, RECEIVE_MMS, RECEIVE_SMS, RECEIVE_WAP_PUSH, SEND_SMS, CALL_PHONE, CALL_PRIVILEGED, PROCESS_OUTGOING_CALLS, READ_CALL_LOG, READ_EXTERNAL_STORAGE, READ_LOGS, ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION, BLUETOOTH, CAMERA, INSTALL_PACKAGES, NFC, READ_CONTACTS, permissionCount, activityCount, serviceCount, receiverCount, providerCount, exportedCount, hPictureCount, mPictureCount, lPictureCount, xPictureCount, totalCount) return fv