这篇文章上次修改于 1252 天前,可能其部分内容已经发生变化,如有疑问可询问作者。

好久没有更新公众号了,为庆祝牛郎织女明天相会,水一篇文章刷下存在感。废话不多说开始正文。

操作环境:Pycharm 的Python console 工具里操作的

Androguard官方文档: https://androguard.readthedocs.io/en/latest/index.html

APK基础信息

image-20210813204424449.png

此处AnalyzeAPK类初始化的时间取决于APK的大小,通常APK越大初始化分析时间就越长。

a.get_package()
Out[71]: 'com.tencent.qqmusic'
a.get_max_sdk_version()

a.get_main_activity()
Out[74]: 'com.tencent.qqmusic.activity.AppStarterActivity'
a.get_app_name()
Out[104]: 'QQ音乐'

APK申请的权限

获取APP静态申请的系统权限:

image-20210813205126462.png

a.get_permissions()
Out[78]: 
['android.permission.RECORD_AUDIO',
 'android.permission.WRITE_EXTERNAL_STORAGE',
 'com.tencent.qqmusic.permission.C2D_MESSAGE',
 'com.tencent.qqmusic.permission.MMOAUTH_CALLBACK',
 'android.permission.WRITE_SETTINGS',
 'android.permission.BROADCAST_STICKY',
 'android.permission.MOUNT_UNMOUNT_FILESYSTEMS',
 'com.android.launcher.permission.INSTALL_SHORTCUT',
 'android.permission.RECEIVE_BOOT_COMPLETED',
 'android.permission.ACCESS_BACKGROUND_LOCATION',
 'android.permission.GET_TASKS',
 'android.permission.READ_PHONE_STATE',
 'com.huawei.android.launcher.permission.WRITE_SETTINGS',
 'android.permission.REORDER_TASKS',
 'android.permission.FOREGROUND_SERVICE',
 'com.asus.msa.SupplementaryDID.ACCESS',
 'com.huawei.appmarket.service.commondata.permission.GET_COMMON_DATA',
 'com.huawei.android.launcher.permission.READ_SETTINGS',
 'com.tencent.qqmusic.permission.MIPUSH_RECEIVE',
 'android.permission.READ_LOGS',
 'android.permission.BLUETOOTH',
 'android.permission.REQUEST_INSTALL_PACKAGES',
 'android.permission.MODIFY_AUDIO_SETTINGS',
 'com.android.launcher.permission.READ_SETTINGS',
 'android.permission.INTERNET',
 'android.permission.WRITE_MEDIA_STORAGE',
 'android.permission.ACCESS_WIFI_STATE',
 'android.permission.ACCESS_FINE_LOCATION',
 'android.permission.CHANGE_WIFI_STATE',
 'android.permission.ACTIVITY_RECOGNITION',
 'android.permission.QUERY_ALL_PACKAGES',
 'com.coloros.mcs.permission.RECIEVE_MCS_MESSAGE',
 'android.permission.qqmusic.qqcbdm',
 'android.permission.BLUETOOTH_ADMIN',
 'android.permission.READ_EXTERNAL_STORAGE',
 'com.android.launcher.permission.UNINSTALL_SHORTCUT',
 'com.meizu.c2dm.permission.RECEIVE',
 'com.oppo.launcher.permission.WRITE_SETTINGS',
 'com.tencent.qqmusic.permission.MM_MESSAGE',
 'android.permission.ACCESS_NETWORK_STATE',
 'android.permission.VIBRATE',
 'android.permission.DISABLE_KEYGUARD',
 'android.permission.EXPAND_STATUS_BAR',
 'android.permission.CHANGE_WIFI_MULTICAST_STATE',
 'android.permission.CHANGE_NETWORK_STATE',
 'com.tencent.qqmusic.permission.PROCESS_PUSH_MSG',
 'com.android.launcher.permission.WRITE_SETTINGS',
 'android.permission.ACCESS_COARSE_LOCATION',
 'android.permission.WRITE_CALENDAR',
 'android.permission.SYSTEM_ALERT_WINDOW',
 'com.heytap.mcs.permission.RECIEVE_MCS_MESSAGE',
 'com.tencent.qqmusic.WNS_PUSH_BROADCAST',
 'com.tencent.qqmusic.push.permission.MESSAGE',
 'android.permission.ACCESS_LOCATION_EXTRA_COMMANDS',
 'com.huawei.android.launcher.permission.CHANGE_BADGE',
 'com.tencent.qqmusic.permission.SEND_BROADCAST_PERMISSION',
 'com.tencent.qqmusic.permission.PUSH_PROVIDER',
 'android.permission.CAMERA',
 'com.oppo.launcher.permission.READ_SETTINGS',
 'android.permission.READ_CALENDAR',
 'com.meizu.flyme.push.permission.RECEIVE',
 'android.permission.RESTART_PACKAGES',
 'com.tencent.qqmusic.theme.permission',
 'android.permission.WAKE_LOCK']
# 该函数能够获取permission的描述信息
a.get_details_permissions()

APK 组件相关

限于篇幅不再贴出四大组件的结果,直接给出相应方法:

Activity相关方法

image-20210813205453620.png

Service相关方法,就一个:

image-20210813205627818.png

Content Providers方法就一个

image-20210813205926338.png

Broadcast Receiver方法也就一个

image-20210813210021266.png

这些方法我们只能获取组件名称无法对其进行细粒度的判断,需要将AndroidManifest.xml转换为字典便于分析。

导入依赖包

import json
import xmltodict

from androguard.misc import AnalyzeAPK
from lxml import etree

转换成字典的方法如下

def manifest_xml2dict(a):
    manifest_xml = a.get_android_manifest_xml()
    manifest_txt = xmltodict.parse(etree.tostring(manifest_xml))
    json_object = json.dumps(manifest_txt)
    manifest_dict = json.loads(json_object)
    return manifest_dict

接下来按照解析字典方式对要分析的组件进行解析按照自定义规则进行安全检测。

APK 签名相关

image-20210813204934488.png

例如我们判断APK文件是否存在Janus漏洞,若只有signed_v1则存在该风险。

a.is_signed_v1()
Out[75]: True
a.is_signed_v2()
Out[76]: True
a.is_signed_v3()
Out[77]: True

证书相关操作方法

image-20210813210335859.png

a.get_signature_names()
Out[88]: ['META-INF/ANDROIDR.RSA']
a.get_signature_names()
Out[89]: ['META-INF/ANDROIDR.RSA']
a.get_signatures()
Out[90]: [b'0\x82\x03\xa0\x06\t*\x86H\x86\xf7\r\x01\x07\x02\xa0\x82\x03\x910\x82\x03\x8d\x02\x01\x011\x0f0\r\x06\t`\x86H\x01e\x03\x04\x02\x01\x05\x000\x0b\x06\t*\x86H\x86\xf7\r\x01\x07\x01\xa0\x82\x02K0\x82\x02G0\x82\x01\xb0\xa0\x03\x02\x01\x02\x02\x04L\xc5D\x9d0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x000g1\x0b0\t\x06\x03U\x04\x06\x13\x02861\x100\x0e\x06\x03U\x04\x08\x13\x07Beijing1\x100\x0e\x06\x03U\x04\x07\x13\x07Beijing1\x100\x0e\x06\x03U\x04\n\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x0b\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x03\x13\x07Tencent0 \x17\r101025084933Z\x18\x0f20601012084933Z0g1\x0b0\t\x06\x03U\x04\x06\x13\x02861\x100\x0e\x06\x03U\x04\x08\x13\x07Beijing1\x100\x0e\x06\x03U\x04\x07\x13\x07Beijing1\x100\x0e\x06\x03U\x04\n\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x0b\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x03\x13\x07Tencent0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x81\x8d\x000\x81\x89\x02\x81\x81\x00\x8b\x9a[\xb7v\r\x14\x88\xdc\xc4|\x1d\x9a\xda.K?\t\x8d9`\xb3\x13\xf7Sw\x0e\xa9{\x90R\x89\x8aC\xc7 !72\x01\xf8I5\xe9\xaf\xf6?LUSM\xedb\x02X\xa6Y\xcae\n\x03o\x83\xc8\xfc\xd19;\xe3\x86\xd1\x0c\xa7\x14M\xc2\x04DG\xf9*\xf3\\\xc4\x06\xf7\x9e1o\xdb\xb6\xac7\x19\xbeQ3\xfakM\xf3\xf6T\xa1\x00\t\x99\xdf\tCm<\x14K}\xac*\xa4\xfd\x0fL2\xaf,\x05\x16\xb4\x1f\x02\x03\x01\x00\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x00\x03\x81\x81\x00PZ<\xf4\x8a\xff\xde>{\xd9/\xb9icG\xab\xaeg@\x08\xde4\xb74\xce\x04\xd1Z\xab2\x0ft\x1c\x15\xde&O6dFV\xfb\x85-\x00\xa7G\xf5\xab\x0f\xb0\xb0&l\x9b\x9c\xdf\xe3\xb82\xc6\x01B\x15>\xbf\xae\xdf\x02\xb4\x1e6\xe2\x95n\x07\x0fv%\x1f\xbce\xd0\xf1-\xf8\x8d\xf6&o\x19N\xb1\xd7[\x892\x11\x94e-\xf2;l\xba\x18~\xdc\r\x9c\xdd{\x1e\xf7\x84\xa9<\xa7q\xdc^;\x87\x14$\x95;\xa91\x82\x01\x190\x82\x01\x15\x02\x01\x010o0g1\x0b0\t\x06\x03U\x04\x06\x13\x02861\x100\x0e\x06\x03U\x04\x08\x13\x07Beijing1\x100\x0e\x06\x03U\x04\x07\x13\x07Beijing1\x100\x0e\x06\x03U\x04\n\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x0b\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x03\x13\x07Tencent\x02\x04L\xc5D\x9d0\r\x06\t`\x86H\x01e\x03\x04\x02\x01\x05\x000\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x04\x81\x80z\x1f\xd9\x9d\xcc\x190\xbc\xa9\x7f\xfd\x8aU\xe4\x89\x17\x02\xcf5K!\x15\x94\x19\r>\x9e\t+H\xd8y\x05f$\xdc)\xa67\xbaW\xad\xb8u\x81J\xa8H$\xd3\x10\x14\xc9\xdf\x8cLA\xca9\x7f\xc0\xc4\x10\xa7\xb4e\x0cuf\xe5(1\xcc\xbb\xb8$\xd4A\xf5\xc2\xd7\x1a\xef\xb8\xc2\x85\x80\xc7\xb1\x82\xc8.\x0e\x8dt\xd0y\xf2#\xb384K\xad\xf1\x00\xac\x80\xd3:1N\xc7\xe5\xa0\xaa.\xff\x9b\xdc\xfc\x92\x08\xe3\x96V\xbd\x07']
a.get_signature_name()
Out[91]: 'META-INF/ANDROIDR.RSA'
a.get_certificates_v1()
Out[92]: [<asn1crypto.x509.Certificate 16372686800 b'0\x82\x02G0\x82\x01\xb0\xa0\x03\x02\x01\x02\x02\x04L\xc5D\x9d0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x000g1\x0b0\t\x06\x03U\x04\x06\x13\x02861\x100\x0e\x06\x03U\x04\x08\x13\x07Beijing1\x100\x0e\x06\x03U\x04\x07\x13\x07Beijing1\x100\x0e\x06\x03U\x04\n\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x0b\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x03\x13\x07Tencent0 \x17\r101025084933Z\x18\x0f20601012084933Z0g1\x0b0\t\x06\x03U\x04\x06\x13\x02861\x100\x0e\x06\x03U\x04\x08\x13\x07Beijing1\x100\x0e\x06\x03U\x04\x07\x13\x07Beijing1\x100\x0e\x06\x03U\x04\n\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x0b\x13\x07Tencent1\x100\x0e\x06\x03U\x04\x03\x13\x07Tencent0\x81\x9f0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x81\x8d\x000\x81\x89\x02\x81\x81\x00\x8b\x9a[\xb7v\r\x14\x88\xdc\xc4|\x1d\x9a\xda.K?\t\x8d9`\xb3\x13\xf7Sw\x0e\xa9{\x90R\x89\x8aC\xc7 !72\x01\xf8I5\xe9\xaf\xf6?LUSM\xedb\x02X\xa6Y\xcae\n\x03o\x83\xc8\xfc\xd19;\xe3\x86\xd1\x0c\xa7\x14M\xc2\x04DG\xf9*\xf3\\\xc4\x06\xf7\x9e1o\xdb\xb6\xac7\x19\xbeQ3\xfakM\xf3\xf6T\xa1\x00\t\x99\xdf\tCm<\x14K}\xac*\xa4\xfd\x0fL2\xaf,\x05\x16\xb4\x1f\x02\x03\x01\x00\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x00\x03\x81\x81\x00PZ<\xf4\x8a\xff\xde>{\xd9/\xb9icG\xab\xaeg@\x08\xde4\xb74\xce\x04\xd1Z\xab2\x0ft\x1c\x15\xde&O6dFV\xfb\x85-\x00\xa7G\xf5\xab\x0f\xb0\xb0&l\x9b\x9c\xdf\xe3\xb82\xc6\x01B\x15>\xbf\xae\xdf\x02\xb4\x1e6\xe2\x95n\x07\x0fv%\x1f\xbce\xd0\xf1-\xf8\x8d\xf6&o\x19N\xb1\xd7[\x892\x11\x94e-\xf2;l\xba\x18~\xdc\r\x9c\xdd{\x1e\xf7\x84\xa9<\xa7q\xdc^;\x87\x14$\x95;\xa9'>]

导入依赖包

from asn1crypto import x509, keys
from oscrypto import asymmetric
import binascii

进行一系列的处理

def get_sign(a):
    sign_info = {}
    try:
        certs = set(
            a.get_certificates_der_v3() + a.get_certificates_der_v2() + [a.get_certificate_der(x) for x in
                                                                         a.get_signature_names()])
        pkeys = set(a.get_public_keys_der_v3() +
                    a.get_public_keys_der_v2())
        for cert in certs:
            x509_cert = x509.Certificate.load(cert)
            sign_info['sign_v1'] = str(a.is_signed_v1()).lower()
            sign_info['sign_v2'] = str(a.is_signed_v2()).lower()
            sign_info['sign_v3'] = str(a.is_signed_v3()).lower()
            sign_info['serial_Number'] = hex(x509_cert.serial_number)
            sign_info['hash_algorithm'] = x509_cert.hash_algo
            sign_info['signature_algorithm'] = x509_cert.signature_algo
            sign_info['valid_not_before'] = str(
                x509_cert['tbs_certificate']['validity']['not_before'].native)
            sign_info['valid_not_after'] = str(
                x509_cert['tbs_certificate']['validity']['not_after'].native)

            if len(certs) > 0:
                print("Found {} unique public keys associated with the certs".format(len(pkeys)))

            for public_key in pkeys:
                x509_public_key = asymmetric.load_public_key(public_key)
                sign_info['issuer'] = x509_public_key.algorithm
                sign_info['bit_size'] = str(x509_public_key.bit_size)
                sign_info['fingerprint'] = binascii.hexlify(
                    x509_public_key.fingerprint).decode('utf-8')
    except Exception as e:
        print(e)
    return sign_info

  # 调用演示
sign=get_sign(a)
Found 1 unique public keys associated with the certs
sign
Out[103]: 
{'sign_v1': 'true',
 'sign_v2': 'true',
 'sign_v3': 'true',
 'serial_Number': '0x4cc5449d',
 'hash_algorithm': 'sha1',
 'signature_algorithm': 'rsassa_pkcs1v15',
 'valid_not_before': '2010-10-25 08:49:33+00:00',
 'valid_not_after': '2060-10-12 08:49:33+00:00',
 'issuer': 'rsa',
 'bit_size': '1024',
 'fingerprint': '036bed21f5938817b0e0bdd4bf066d748660b5494aaab83c93889e79e4104812'}

APK 交叉引用分析

获取所有的类:dx.get_classes()

获取所有的方法:dx.get_methods()

获取所有的字符串:dx.get_strings()

可以获取某个方法的调用链。例如分析获取剪切板内容函数的调用分析

for method in dx.find_methods(classname='Landroid/content/ClipboardManager;', methodname='getPrimaryClip'):
    for call in method.get_xref_from():
        print(call[1].__str__().split('@')[0])
        # 若想打印 代码块可以使用如下方法
        # print(call[1].get_source())
    
Lcom/tencent/qqmusic/clipboard/b;->a()Ljava/lang/String; [access_flags=private final] 
Lcom/tencent/mobileqq/webviewplugin/plugins/d;->a(Ljava/lang/String; Ljava/lang/String; Ljava/lang/String; [Ljava/lang/String;)Z [access_flags=protected varargs] 
Lcom/tencent/qqmusic/business/timeline/post/mention/MentionEditText;->a(Ljava/lang/String;)I [access_flags=private final] 
Lcom/tencent/mtt/hippy/modules/nativemodules/clipboard/ClipboardModule;->getString(Lcom/tencent/mtt/hippy/modules/Promise;)V [access_flags=public] 
Lcom/tencent/qqmusic/business/timeline/post/mention/MentionEditText;->getClipboardAndClipStr()Lkotlin/Pair; [access_flags=public final] 
Lcom/tencent/qqmusic/business/timeline/post/mention/MentionEditText;->a(Ljava/lang/String;)I [access_flags=private final] 
Lcom/tencent/qqmusic/redpacket/d;->a(Lcom/tencent/qqmusic/activity/baseactivity/BaseActivity;)V [access_flags=public final] 
Lcom/tencent/qqmusic/clipboard/b;->a()Ljava/lang/String; [access_flags=private final] 
Ljackpal/androidterm/emulatorview/a/g;->a()Ljava/lang/CharSequence; [access_flags=public] 
Lcom/tencent/mtt/hippy/modules/nativemodules/clipboard/ClipboardModule;->getString(Lcom/tencent/mtt/hippy/modules/Promise;)V [access_flags=public] 
Ljackpal/androidterm/emulatorview/a/g;->b()Z [access_flags=public] 

检测到11处调用该方法。

对漏洞的检测只要更新类名和方法名即可。重要的是检测规则。

小结

1.库的使用很简单,有时候库中方法的输出达不到我们的预期输出,则需要查看一下代码,我们重新改写下即可。
2.库的交叉引用很方便的查找到目标方法的调用链、调用代码块。其实对APK漏洞的检测只要替换类名和方法名即可。替换为存在安全问题的类和相应类中方法即可。
3.Androguard 也不是没有缺点。目前对内部类,接口实现类的调用链检测不太友好。