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
Exemple #2
0
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))
Exemple #3
0
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)
Exemple #4
0
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