# -*- coding: utf-8 -*- import unittest import inspect import os import sys import glob import hashlib # ensure that androguard is loaded from this source code localmodule = os.path.realpath( os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..')) if localmodule not in sys.path: sys.path.insert(0, localmodule) from androguard.core.bytecodes import apk print('loaded from', apk.__file__) class APKTest(unittest.TestCase): def testAPK(self): for f in ["examples/android/TestsAndroguard/bin/TestActivity.apk"] \ + glob.glob('examples/tests/*.apk'): with open(f, "rb") as fd: a = apk.APK(fd.read(), True) self.assertTrue(a) def testAPKWrapper(self): from androguard.misc import AnalyzeAPK from androguard.core.bytecodes.apk import APK from androguard.core.bytecodes.dvm import DEX from androguard.core.analysis.analysis import Analysis a, d, dx = AnalyzeAPK("examples/android/TestsAndroguard/bin/TestActivity.apk") self.assertIsInstance(a, APK) self.assertIsInstance(d[0], DEX) self.assertIsInstance(dx, Analysis) self.assertEqual(a.get_signature_name(), "META-INF/CERT.RSA") self.assertEqual(a.get_signature_names(), ["META-INF/CERT.RSA"]) self.assertIsNotNone(a.get_certificate(a.get_signature_name())) def testAPKWrapperRaw(self): from androguard.misc import AnalyzeAPK from androguard.core.bytecodes.apk import APK from androguard.core.bytecodes.dvm import DEX from androguard.core.analysis.analysis import Analysis with open( "examples/android/TestsAndroguard/bin/TestActivity.apk", 'rb') \ as file_obj: file_contents = file_obj.read() a, d, dx = AnalyzeAPK(file_contents, raw=True) self.assertIsInstance(a, APK) self.assertIsInstance(d[0], DEX) self.assertIsInstance(dx, Analysis) self.assertEqual(a.get_signature_name(), "META-INF/CERT.RSA") self.assertEqual(a.get_signature_names(), ["META-INF/CERT.RSA"]) self.assertIsNotNone(a.get_certificate(a.get_signature_name())) def testMultiDexAPK(self): from androguard.misc import AnalyzeAPK from androguard.core.bytecodes.apk import APK from androguard.core.bytecodes.dvm import DEX from androguard.core.analysis.analysis import Analysis a, d, dx = AnalyzeAPK('examples/android/abcore/app-prod-debug.apk') self.assertIsInstance(a, APK) self.assertIsInstance(d[0], DEX) self.assertIsInstance(d[1], DEX) self.assertIsInstance(dx, Analysis) def testAPKCert(self): """ Test if certificates are correctly unpacked from the SignatureBlock files :return: """ from androguard.core.bytecodes.apk import APK import binascii a = APK("examples/android/TestsAndroguard/bin/TestActivity.apk", skip_analysis=True) cert = a.get_certificate_der(a.get_signature_name()) expected = "308201E53082014EA00302010202045114FECF300D06092A864886F70D010105" \ "05003037310B30090603550406130255533110300E060355040A1307416E6472" \ "6F6964311630140603550403130D416E64726F6964204465627567301E170D31" \ "33303230383133333430375A170D3433303230313133333430375A3037310B30" \ "090603550406130255533110300E060355040A1307416E64726F696431163014" \ "0603550403130D416E64726F696420446562756730819F300D06092A864886F7" \ "0D010101050003818D00308189028181009903975EC93F0F3CCB54BD1A415ECF" \ "3505993715B8B9787F321104ACC7397D186F01201341BCC5771BB28695318E00" \ "6E47C888D3C7EE9D952FF04DF06EDAB1B511F51AACDCD02E0ECF5AA7EC6B51BA" \ "08C601074CF2DA579BD35054E4F77BAAAAF0AA67C33C1F1C3EEE05B5862952C0" \ "888D39179C0EDD785BA4F47FB7DF5D5F030203010001300D06092A864886F70D" \ "0101050500038181006B571D685D41E77744F5ED20822AE1A14199811CE649BB" \ "B29248EB2F3CC7FB70F184C2A3D17C4F86B884FCA57EEB289ECB5964A1DDBCBD" \ "FCFC60C6B7A33D189927845067C76ED29B42D7F2C7F6E2389A4BC009C01041A3" \ "6E666D76D1D66467416E68659D731DC7328CB4C2E989CF59BB6D2D2756FDE7F2" \ "B3FB733EBB4C00FD3B" self.assertEqual(binascii.hexlify(cert).decode("ascii").upper(), expected) def testAPKCertFingerprint(self): """ Test if certificates are correctly unpacked from the SignatureBlock files Check if fingerprints matches :return: """ from androguard.core.bytecodes.apk import APK import binascii from hashlib import md5, sha1, sha256 a = APK("examples/android/TestsAndroguard/bin/TestActivity.apk", skip_analysis=True) # this one is not signed v2, it is v1 only self.assertTrue(a.is_signed_v1()) self.assertFalse(a.is_signed_v2()) self.assertTrue(a.is_signed()) self.assertEqual(a.get_certificates_der_v2(), []) self.assertEqual(a.get_certificates_v2(), []) self.assertEqual(a.get_signature_name(), "META-INF/CERT.RSA") self.assertEqual(a.get_signature_names(), ["META-INF/CERT.RSA"]) cert = a.get_certificate(a.get_signature_name()) cert_der = a.get_certificate_der(a.get_signature_name()) # Keytool are the hashes collected by keytool -printcert -file CERT.RSA for h2, keytool in [(md5, "99:FF:FC:37:D3:64:87:DD:BA:AB:F1:7F:94:59:89:B5"), (sha1, "1E:0B:E4:01:F9:34:60:E0:8D:89:A3:EF:6E:27:25:55:6B:E1:D1:6B"), (sha256, "6F:5C:31:60:8F:1F:9E:28:5E:B6:34:3C:7C:8A:F0:7D:E8:1C:1F:B2:14:8B:53:49:BE:C9:06:44:41:44:57:6D")]: x = h2() x.update(cert_der) hash_hashlib = x.hexdigest() self.assertEqual(hash_hashlib.lower(), keytool.replace(":", "").lower()) def testAPKv2Signature(self): from androguard.core.bytecodes.apk import APK a = APK("examples/signing/TestActivity_signed_both.apk") self.assertTrue(a.is_signed_v1()) self.assertTrue(a.is_signed_v2()) self.assertTrue(a.is_signed()) # Signing name is maximal 8 chars... self.assertEqual(a.get_signature_name(), "META-INF/ANDROGUA.RSA") self.assertEqual(len(a.get_certificates_der_v2()), 1) # As we signed with the same certificate, both methods should return the # same content self.assertEqual(a.get_certificate_der(a.get_signature_name()), a.get_certificates_der_v2()[0]) from asn1crypto import x509 self.assertIsInstance(a.get_certificates_v2()[0], x509.Certificate) # Test if the certificate is also the same as on disk with open("examples/signing/certificate.der", "rb") as f: cert = f.read() cert_der_v1 = a.get_certificate_der(a.get_signature_name()) cert_der_v2 = a.get_certificates_der_v2()[0] for fun in [hashlib.md5, hashlib.sha1, hashlib.sha256, hashlib.sha512]: h1 = fun(cert).hexdigest() h2 = fun(cert_der_v1).hexdigest() h3 = fun(cert_der_v2).hexdigest() self.assertEqual(h1, h2) self.assertEqual(h1, h3) self.assertEqual(h2, h3) def testApksignAPKs(self): # These APKs are from the apksign testcases and cover # all different signature algorithms as well as some error cases from androguard.core.bytecodes.apk import APK import zipfile from asn1crypto import x509, pem import binascii root = "examples/signing/apksig" # Correct values generated with openssl: # In the apksig repo:src/test/resources/com/android/apksig # for f in *.pem; do openssl x509 -in $f -noout -sha256 -fingerprint; done certfp = { 'dsa-1024.x509.pem': 'fee7c19ff9bfb4197b3727b9fd92d95406b1bd96db99ea642f5faac019a389d7', 'dsa-2048.x509.pem': '97cce0bab292c2d5afb9de90e1810b41a5d25c006a10d10982896aa12ab35a9e', 'dsa-3072.x509.pem': '966a4537058d24098ea213f12d4b24e37ff5a1d8f68deb8a753374881f23e474', 'ec-p256.x509.pem': '6a8b96e278e58f62cfe3584022cec1d0527fcb85a9e5d2e1694eb0405be5b599', 'ec-p384.x509.pem': '5e7777ada7ee7ce8f9c4d1b07094876e5604617b7988b4c5d5b764a23431afbe', 'ec-p521.x509.pem': '69b50381d98bebcd27df6d7df8af8c8b38d0e51e9168a95ab992d1a9da6082da', 'rsa-1024_2.x509.pem': 'eba3685e799f59804684abebf0363e14ccb1c213e2b954a22669714ed97f61e9', 'rsa-1024.x509.pem': 'bc5e64eab1c4b5137c0fbc5ed05850b3a148d1c41775cffa4d96eea90bdd0eb8', 'rsa-16384.x509.pem': 'f3c6b37909f6df310652fbd7c55ec27d3079dcf695dc6e75e22ba7c4e1c95601', 'rsa-2048_2.x509.pem': '681b0e56a796350c08647352a4db800cc44b2adc8f4c72fa350bd05d4d50264d', 'rsa-2048_3.x509.pem': 'bb77a72efc60e66501ab75953af735874f82cfe52a70d035186a01b3482180f3', 'rsa-2048.x509.pem': 'fb5dbd3c669af9fc236c6991e6387b7f11ff0590997f22d0f5c74ff40e04fca8', 'rsa-3072.x509.pem': '483934461229a780010bc07cd6eeb0b67025fc4fe255757abbf5c3f2ed249e89', 'rsa-4096.x509.pem': '6a46158f87753395a807edcc7640ac99c9125f6b6e025bdbf461ff281e64e685', 'rsa-8192.x509.pem': '060d0a24fea9b60d857225873f78838e081795f7ef2d1ea401262bbd75a58234', } will_not_validate_correctly = [ "targetSandboxVersion-2.apk", "targetSandboxVersion-2.apk", "v1-only-with-cr-in-entry-name.apk", "v1-only-with-lf-in-entry-name.apk", "v1-only-with-nul-in-entry-name.apk", "v1-only-with-rsa-1024-cert-not-der2.apk", "v2-only-cert-and-public-key-mismatch.apk", "v2-only-with-dsa-sha256-1024-sig-does-not-verify.apk", "debuggable-boolean.apk", "debuggable-resource.apk", ] # Collect possible hashes for certificates # Unfortunately, not all certificates are supplied... for apath in os.listdir(root): if apath in certfp: with open(os.path.join(root, apath), "rb") as fp: cert = x509.Certificate.load(pem.unarmor(fp.read())[2]) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertEqual(h, certfp[apath]) self.assertIn(h, certfp.values()) for apath in os.listdir(root): if apath.endswith(".apk"): if apath == "v2-only-garbage-between-cd-and-eocd.apk" or \ apath == "v2-only-truncated-cd.apk" or \ apath == "v1v2v3-with-rsa-2048-lineage-3-signers-invalid-zip.apk": # Can not load as APK with self.assertRaises(zipfile.BadZipFile): APK(os.path.join(root, apath)) continue elif apath in will_not_validate_correctly: # These APKs are faulty (by design) and will return a not correct fingerprint. # TODO: we need to check if we can prevent such errors... continue a = APK(os.path.join(root, apath)) self.assertIsInstance(a, APK) # Test if the correct method returns True, while others return # False m_tests = {'1': a.is_signed_v1, '2': a.is_signed_v2, '3': a.is_signed_v3} # These APKs will raise an error excluded = [ "v1v2v3-with-rsa-2048-lineage-3-signers-no-sig-block.apk", "v2-only-apk-sig-block-size-mismatch.apk", "v2-only-empty.apk", "v2-only-wrong-apk-sig-block-magic.apk", "v2-stripped.apk", "v2-stripped-with-ignorable-signing-schemes.apk", "v2v3-signed-v3-block-stripped.apk", "v3-only-empty.apk", "v3-only-with-ecdsa-sha512-p384-wrong-apk-sig-block-magic.apk", "v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk", "v3-stripped.apk", ] if apath[0] == "v" and apath not in excluded: methods = apath.split("-", 1)[0].split("v")[1:] for m, f in m_tests.items(): if m in methods: self.assertTrue(f()) else: self.assertFalse(f()) # Special error cases if apath == "v2-only-apk-sig-block-size-mismatch.apk": with self.assertRaises(apk.BrokenAPKError): a.is_signed_v2() continue elif apath == "v2-only-empty.apk": with self.assertRaises(apk.BrokenAPKError): a.is_signed_v2() continue elif apath == "v3-only-with-rsa-pkcs1-sha512-4096-apk-sig-block-size-mismatch.apk": with self.assertRaises(apk.BrokenAPKError): a.is_signed_v3() continue if a.is_signed_v1(): if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): for c in a.get_signature_names(): a.get_certificate(c) elif apath == "v1-only-with-rsa-1024-cert-not-der.apk": for sig in a.get_signature_names(): c = a.get_certificate(sig) h = c.sha256_fingerprint.replace(" ","").lower() self.assertNotIn(h, certfp.values()) # print([apath, h]) # I do not know, why put this file? der = a.get_certificate_der(sig) apk.show_Certificate(c, True) apk.show_Certificate(c, False) self.assertEqual(hashlib.sha256(der).hexdigest(), h) pass else: for sig in a.get_signature_names(): c = a.get_certificate(sig) h = c.sha256_fingerprint.replace(" ","").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER der = a.get_certificate_der(sig) self.assertEqual(hashlib.sha256(der).hexdigest(), h) if a.is_signed_v2(): if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): a.get_certificates_der_v2() elif apath == "v2-only-with-rsa-pkcs1-sha256-1024-cert-not-der.apk": # FIXME # Not sure what this one should do... but the certificate fingerprint is weird # as the hash over the DER is not the same when using the certificate continue else: for c in a.get_certificates_der_v2(): cert = x509.Certificate.load(c) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER self.assertEqual(hashlib.sha256(c).hexdigest(), h) if a.is_signed_v3(): print(apath) if apath == "weird-compression-method.apk": with self.assertRaises(NotImplementedError): a.get_certificates_der_v3() elif apath == "v3-only-with-rsa-pkcs1-sha256-3072-sig-does-not-verify.apk" or \ apath == "v3-only-cert-and-public-key-mismatch.apk": cert = x509.Certificate.load(a.get_certificates_der_v3()[0]) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertNotIn(h, certfp.values()) else: for c in a.get_certificates_der_v3(): cert = x509.Certificate.load(c) h = cert.sha256_fingerprint.replace(" ","").lower() self.assertIn(h, certfp.values()) # Check that we get the same signature if we take the DER self.assertEqual(hashlib.sha256(c).hexdigest(), h) def testAPKWrapperUnsigned(self): from androguard.misc import AnalyzeAPK from androguard.core.bytecodes.apk import APK from androguard.core.bytecodes.dvm import DEX from androguard.core.analysis.analysis import Analysis a, d, dx = AnalyzeAPK("examples/android/TestsAndroguard/bin/TestActivity_unsigned.apk") self.assertIsInstance(a, APK) self.assertIsInstance(d[0], DEX) self.assertIsInstance(dx, Analysis) self.assertIsNone(a.get_signature_name()) self.assertEqual(a.get_signature_names(), []) def testAPKManifest(self): from androguard.core.bytecodes.apk import APK a = APK("examples/android/TestsAndroguard/bin/TestActivity.apk", testzip=True) self.assertEqual(a.get_app_name(), "TestsAndroguardApplication") self.assertEqual(a.get_app_icon(), "res/drawable-hdpi/icon.png") self.assertEqual(a.get_app_icon(max_dpi=120), "res/drawable-ldpi/icon.png") self.assertEqual(a.get_app_icon(max_dpi=160), "res/drawable-mdpi/icon.png") self.assertEqual(a.get_app_icon(max_dpi=240), "res/drawable-hdpi/icon.png") self.assertIsNone(a.get_app_icon(max_dpi=1)) self.assertEqual(a.get_main_activity(), "tests.androguard.TestActivity") self.assertEqual(a.get_package(), "tests.androguard") self.assertEqual(a.get_androidversion_code(), '1') self.assertEqual(a.get_androidversion_name(), "1.0") self.assertEqual(a.get_min_sdk_version(), "9") self.assertEqual(a.get_target_sdk_version(), "16") self.assertIsNone(a.get_max_sdk_version()) self.assertEqual(a.get_permissions(), []) self.assertEqual(a.get_declared_permissions(), []) self.assertTrue(a.is_valid_APK()) def testAPKPermissions(self): from androguard.core.bytecodes.apk import APK a = APK("examples/tests/a2dp.Vol_137.apk", testzip=True) self.assertEqual(a.get_package(), "a2dp.Vol") self.assertListEqual(sorted(a.get_permissions()), sorted(["android.permission.RECEIVE_BOOT_COMPLETED", "android.permission.CHANGE_WIFI_STATE", "android.permission.ACCESS_WIFI_STATE", "android.permission.KILL_BACKGROUND_PROCESSES", "android.permission.BLUETOOTH", "android.permission.BLUETOOTH_ADMIN", "com.android.launcher.permission.READ_SETTINGS", "android.permission.RECEIVE_SMS", "android.permission.MODIFY_AUDIO_SETTINGS", "android.permission.READ_CONTACTS", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS", "android.permission.WRITE_EXTERNAL_STORAGE", "android.permission.READ_PHONE_STATE", "android.permission.BROADCAST_STICKY", "android.permission.GET_ACCOUNTS"])) def testAPKActivitiesAreString(self): from androguard.core.bytecodes.apk import APK a = APK("examples/tests/a2dp.Vol_137.apk", testzip=True) activities = a.get_activities() self.assertTrue(isinstance(activities[0], str), 'activities[0] is not of type str') def testAPKIntentFilters(self): from androguard.core.bytecodes.apk import APK a = APK("examples/tests/a2dp.Vol_137.apk", testzip=True) activities = a.get_activities() receivers = a.get_receivers() services = a.get_services() filter_list = [] for i in activities: filters = a.get_intent_filters("activity", i) if len(filters) > 0: filter_list.append(filters) for i in receivers: filters = a.get_intent_filters("receiver", i) if len(filters) > 0: filter_list.append(filters) for i in services: filters = a.get_intent_filters("service", i) if len(filters) > 0: filter_list.append(filters) pairs = zip(filter_list, [{ 'action': ['android.intent.action.MAIN'], 'category': ['android.intent.category.LAUNCHER'] }, { 'action': [ 'android.intent.action.BOOT_COMPLETED', 'android.intent.action.MY_PACKAGE_REPLACED' ], 'category': ['android.intent.category.HOME'] }, { 'action': ['android.appwidget.action.APPWIDGET_UPDATE'] }, { 'action': ['android.service.notification.NotificationListenerService'] }]) self.assertFalse(any(x != y for x, y in pairs)) a = APK("examples/tests/com.test.intent_filter.apk", testzip=True) activities = a.get_activities() activities = a.get_activities() receivers = a.get_receivers() services = a.get_services() filter_list = [] for i in activities: filters = a.get_intent_filters("activity", i) if len(filters) > 0: filter_list.append(filters) for i in receivers: filters = a.get_intent_filters("receiver", i) if len(filters) > 0: filter_list.append(filters) for i in services: filters = a.get_intent_filters("service", i) if len(filters) > 0: filter_list.append(filters) pairs = zip(filter_list, [{ 'action': ['android.intent.action.VIEW'], 'category': [ 'android.intent.category.APP_BROWSER', 'android.intent.category.DEFAULT', 'android.intent.category.BROWSABLE' ], 'data': [{ 'scheme': 'testscheme', 'host': 'testhost', 'port': '0301', 'path': '/testpath', 'pathPattern': 'testpattern', 'mimeType': 'text/html' }] }, { 'action': ['android.intent.action.MAIN'], 'category': ['android.intent.category.LAUNCHER'] }, { 'action': ['android.intent.action.VIEW'], 'category': ['android.intent.category.DEFAULT', 'android.intent.category.BROWSABLE'], 'data': [{ 'scheme': 'testhost', 'host': 'testscheme', 'port': '0301', 'path': '/testpath', 'pathPattern': 'testpattern', 'mimeType': 'text/html' }] }, { 'action': ['android.intent.action.RESPOND_VIA_MESSAGE'], 'data': [{ 'scheme': 'testhost', 'host': 'testscheme', 'port': '0301', 'path': '/testpath', 'pathPattern': 'testpattern', 'mimeType': 'text/html' }, { 'scheme': 'testscheme2', 'host': 'testhost2', 'port': '0301', 'path': '/testpath2', 'pathPattern': 'testpattern2', 'mimeType': 'image/png' }] }]) self.assertFalse(any(x != y for x, y in pairs)) def testEffectiveTargetSdkVersion(self): from androguard.core.bytecodes.apk import APK a = APK('examples/android/abcore/app-prod-debug.apk') self.assertEqual(27, a.get_effective_target_sdk_version()) a = APK('examples/android/Invalid/Invalid.apk') self.assertEqual(15, a.get_effective_target_sdk_version()) a = APK('examples/android/TC/bin/TC-debug.apk') self.assertEqual(1, a.get_effective_target_sdk_version()) a = APK('examples/android/TCDiff/bin/TCDiff-debug.apk') self.assertEqual(1, a.get_effective_target_sdk_version()) a = APK('examples/android/TestsAndroguard/bin/TestActivity.apk') self.assertEqual(16, a.get_effective_target_sdk_version()) a = APK('examples/android/TestsAndroguard/bin/TestActivity_unsigned.apk') self.assertEqual(16, a.get_effective_target_sdk_version()) a = APK('examples/dalvik/test/bin/Test-debug.apk') self.assertEqual(1, a.get_effective_target_sdk_version()) a = APK('examples/dalvik/test/bin/Test-debug-unaligned.apk') self.assertEqual(1, a.get_effective_target_sdk_version()) a = APK('examples/tests/a2dp.Vol_137.apk') self.assertEqual(25, a.get_effective_target_sdk_version()) a = APK('examples/tests/hello-world.apk') self.assertEqual(25, a.get_effective_target_sdk_version()) a = APK('examples/tests/duplicate.permisssions_9999999.apk') self.assertEqual(27, a.get_effective_target_sdk_version()) a = APK('examples/tests/com.politedroid_4.apk') self.assertEqual(3, a.get_effective_target_sdk_version()) def testUsesImpliedPermissions(self): from androguard.core.bytecodes.apk import APK a = APK('examples/android/abcore/app-prod-debug.apk') self.assertEqual([['android.permission.READ_EXTERNAL_STORAGE', None],], a.get_uses_implied_permission_list()) a = APK('examples/android/Invalid/Invalid.apk') self.assertEqual([], a.get_uses_implied_permission_list()) a = APK('examples/android/TC/bin/TC-debug.apk') self.assertEqual([['android.permission.WRITE_EXTERNAL_STORAGE', None], ['android.permission.READ_PHONE_STATE', None], ['android.permission.READ_EXTERNAL_STORAGE', None],], a.get_uses_implied_permission_list()) a = APK('examples/android/TCDiff/bin/TCDiff-debug.apk') self.assertEqual([['android.permission.WRITE_EXTERNAL_STORAGE', None], ['android.permission.READ_PHONE_STATE', None], ['android.permission.READ_EXTERNAL_STORAGE', None],], a.get_uses_implied_permission_list()) a = APK('examples/android/TestsAndroguard/bin/TestActivity.apk') self.assertEqual([], a.get_uses_implied_permission_list()) a = APK('examples/android/TestsAndroguard/bin/TestActivity_unsigned.apk') self.assertEqual([], a.get_uses_implied_permission_list()) a = APK('examples/dalvik/test/bin/Test-debug.apk') self.assertEqual([['android.permission.WRITE_EXTERNAL_STORAGE', None], ['android.permission.READ_PHONE_STATE', None], ['android.permission.READ_EXTERNAL_STORAGE', None],], a.get_uses_implied_permission_list()) a = APK('examples/dalvik/test/bin/Test-debug-unaligned.apk') self.assertEqual([['android.permission.WRITE_EXTERNAL_STORAGE', None], ['android.permission.READ_PHONE_STATE', None], ['android.permission.READ_EXTERNAL_STORAGE', None],], a.get_uses_implied_permission_list()) a = APK('examples/tests/a2dp.Vol_137.apk') self.assertEqual([['android.permission.READ_EXTERNAL_STORAGE', None],], a.get_uses_implied_permission_list()) a = APK('examples/tests/com.politedroid_4.apk') self.assertEqual([['android.permission.WRITE_EXTERNAL_STORAGE', None], ['android.permission.READ_PHONE_STATE', None], ['android.permission.READ_EXTERNAL_STORAGE', None],], a.get_uses_implied_permission_list()) a = APK('examples/tests/duplicate.permisssions_9999999.apk') self.assertEqual([['android.permission.READ_EXTERNAL_STORAGE', 18],], a.get_uses_implied_permission_list()) a = APK('examples/tests/hello-world.apk') self.assertEqual([], a.get_uses_implied_permission_list()) a = APK('examples/tests/urzip-πÇÇπÇÇ现代汉语通用字-български-عربي1234.apk') self.assertEqual([], a.get_uses_implied_permission_list()) def testNewZipWithoutModification(self): from androguard.core.bytecodes.apk import APK try: from unittest.mock import patch, MagicMock except: from mock import patch, MagicMock a = APK("examples/tests/a2dp.Vol_137.apk", testzip=True) with patch('zipfile.ZipFile') as zipFile: mockZip = MagicMock() zipFile.return_value=mockZip a.new_zip("testout.apk") self.assertEqual(mockZip.writestr.call_count, 48) self.assertTrue(mockZip.close.called) def testNewZipWithDeletedFile(self): from androguard.core.bytecodes.apk import APK try: from unittest.mock import patch, MagicMock except: from mock import patch, MagicMock a = APK("examples/tests/a2dp.Vol_137.apk", testzip=True) with patch('zipfile.ZipFile') as zipFile: mockZip = MagicMock() zipFile.return_value=mockZip a.new_zip("testout.apk", deleted_files="res/menu/menu.xml") self.assertEqual(mockZip.writestr.call_count, 47) self.assertTrue(mockZip.close.called) def testNewZipWithNewFile(self): from androguard.core.bytecodes.apk import APK try: from unittest.mock import patch, MagicMock except: from mock import patch, MagicMock a = APK("examples/tests/a2dp.Vol_137.apk", testzip=True) with patch('zipfile.ZipFile') as zipFile: mockZip = MagicMock() zipFile.return_value=mockZip a.new_zip("testout.apk", new_files={'res/menu/menu.xml': 'content'}) self.assertEqual(mockZip.writestr.call_count, 48) self.assertTrue(mockZip.close.called) def testFeatures(self): from androguard.core.bytecodes.apk import APK # First Demo App a = APK("examples/tests/com.example.android.tvleanback.apk") self.assertListEqual(list(a.get_features()), ["android.hardware.microphone", "android.hardware.touchscreen", "android.software.leanback"]) self.assertTrue(a.is_androidtv()) self.assertFalse(a.is_wearable()) self.assertTrue(a.is_leanback()) # Second Demo App a = APK("examples/tests/com.example.android.wearable.wear.weardrawers.apk") self.assertListEqual(list(a.get_features()), ["android.hardware.type.watch"]) self.assertTrue(a.is_wearable()) self.assertFalse(a.is_leanback()) self.assertFalse(a.is_androidtv()) self.assertListEqual(list(a.get_libraries()), ["com.google.android.wearable"]) def testAdaptiveIcon(self): # See https://developer.android.com/guide/practices/ui_guidelines/icon_design_adaptive.html from androguard.core.bytecodes.apk import APK from androguard.core.bytecodes.axml import AXMLPrinter a = APK("examples/tests/com.android.example.text.styling.apk") self.assertEqual(a.get_app_icon(), "res/mipmap-anydpi-v26/ic_launcher.xml") x = AXMLPrinter(a.get_file(a.get_app_icon())).get_xml().decode("UTF-8") self.assertIn("adaptive-icon", x) # * ldpi (low) ~120dpi # * mdpi (medium) ~160dpi # * hdpi (high) ~240dpi # * xhdpi (extra-high) ~320dpi # * xxhdpi (extra-extra-high) ~480dpi # * xxxhdpi (extra-extra-extra-high) ~640dpi self.assertIsNone(a.get_app_icon(max_dpi=120)) # No LDPI icon self.assertIn("mdpi", a.get_app_icon(max_dpi=160)) self.assertIn("hdpi", a.get_app_icon(max_dpi=240)) self.assertIn("xhdpi", a.get_app_icon(max_dpi=320)) self.assertIn("xxhdpi", a.get_app_icon(max_dpi=480)) self.assertIn("xxxhdpi", a.get_app_icon(max_dpi=640)) self.assertIn(".png", a.get_app_icon(max_dpi=65533)) self.assertIn(".xml", a.get_app_icon(max_dpi=65534)) def testPartialSignature(self): from androguard.core.bytecodes.apk import APK a = APK("examples/tests/partialsignature.apk", skip_analysis=True) self.assertIn("META-INF/CERT.RSA", a.get_files()) self.assertIn("META-INF/6AD89F48.RSA", a.get_files()) self.assertNotIn("META-INF/CERT.RSA", a.get_signature_names()) self.assertIn("META-INF/6AD89F48.RSA", a.get_signature_names()) def testFrameworkResAPK(self): from androguard.core.bytecodes.apk import APK a = APK("examples/tests/lineageos_nexus5_framework-res.apk") self.assertEqual(a.get_app_name(), 'Android System') self.assertEqual(a.get_package(), 'android') def testMagic(self): """Test if the correct magic package is installed""" import magic self.assertTrue(hasattr(magic, 'MagicException')) self.assertTrue(hasattr(magic, 'from_buffer')) def testPermissionLoading(self): """Test if fallbacks for permission lists are working""" from androguard.core.api_specific_resources import load_permissions from androguard.core.androconf import load_api_specific_resource_module, InvalidResourceError, CONF import re root = 'androguard/core/api_specific_resources' levels = filter(lambda x: re.match(r'^permissions_\d+\.json$', x), os.listdir(os.path.join(root, "aosp_permissions"))) levels = list(map(lambda x: int(x[:-5].split('_')[1]), levels)) min_level = min(levels) max_level = max(levels) self.assertGreater(min_level, 0) self.assertGreater(max_level, 0) self.assertNotEqual(load_permissions(min_level), {}) self.assertNotEqual(load_permissions(min_level, 'groups'), {}) self.assertNotEqual(load_permissions(max_level), {}) self.assertNotEqual(load_permissions(max_level, 'groups'), {}) self.assertNotEqual(load_permissions(max_level - 1), {}) self.assertNotEqual(load_permissions(max_level - 1, 'groups'), {}) self.assertNotEqual(load_permissions(min_level + 1), {}) self.assertNotEqual(load_permissions(min_level + 1, 'groups'), {}) self.assertEqual(load_permissions(min_level - 1), load_permissions(min_level)) self.assertEqual(load_permissions(max_level + 1), load_permissions(max_level)) self.assertEqual(load_permissions(0), load_permissions(min_level)) self.assertEqual(load_permissions(1337), load_permissions(max_level)) with self.assertRaises(ValueError): load_permissions(23, 'foobar') with self.assertRaises(InvalidResourceError): load_api_specific_resource_module('blablabla') self.assertEqual(load_permissions(16), load_api_specific_resource_module('aosp_permissions', 16)) self.assertEqual(load_permissions(CONF['DEFAULT_API']), load_api_specific_resource_module('aosp_permissions')) for level in levels: perm = load_permissions(level) self.assertIn('android.permission.INTERNET', perm) self.assertIsInstance(perm, dict) self.assertIsInstance(perm['android.permission.INTERNET'], dict) self.assertIn('description', perm['android.permission.INTERNET']) self.assertIn('label', perm['android.permission.INTERNET']) self.assertIn('protectionLevel', perm['android.permission.INTERNET']) self.assertIn('permissionGroup', perm['android.permission.INTERNET']) def testShortNamesInManifest(self): """Test if shortnames are correctly handled""" a = apk.APK("examples/axml/AndroidManifest_ShortName.apk") self.assertEqual(a.get_package(), 'com.android.galaxy4') self.assertEqual(len(a.get_activities()), 1) self.assertEqual(len(a.get_services()), 1) self.assertEqual(a.get_activities()[0], 'com.android.galaxy4.Galaxy4') self.assertEqual(a.get_services()[0], 'com.android.galaxy4.Galaxy4Wallpaper') self.assertEqual(list(a.get_all_attribute_value("activity", "name"))[0], 'com.android.galaxy4.Galaxy4') self.assertEqual(list(a.get_all_attribute_value("activity", "name", format_value=False))[0], '.Galaxy4') # Test some formattings self.assertEqual(a._format_value('foo'), 'com.android.galaxy4.foo') self.assertEqual(a._format_value('.foo'), 'com.android.galaxy4.foo') self.assertEqual(a._format_value('com.android.galaxy4.foo'), 'com.android.galaxy4.foo') self.assertEqual(a._format_value('bla.bar.foo'), 'bla.bar.foo') self.assertEqual(a._format_value(None), None) a.package = None self.assertEqual(a._format_value('foo'), 'foo') self.assertEqual(a._format_value('.foo'), '.foo') self.assertEqual(a._format_value('com.android.galaxy4.foo'), 'com.android.galaxy4.foo') self.assertEqual(a._format_value('bla.bar.foo'), 'bla.bar.foo') self.assertEqual(a._format_value(None), None) if __name__ == '__main__': unittest.main(failfast=True)