Exemple #1
0
    def test_init(self):
        for filename in self.certs:
            self.assertTrue(self.certs[filename] is not None)
            self.assertTrue(type(self.certs[filename]) is Cert)

        # Test the class raise when a non-existing file is given:
        with self.assertRaises(ParsingError):
            Cert(join("tests", "data", "aaa_this_is_a_non_existent_file_xxx"))

        # Test the class raise when a non-CERT.RSA file is given:
        with self.assertRaises(CertParsingError):
            Cert(join("tests", "data", "Example.apk"))
            Cert(join("tests", "data", "AndroidManifest.xml"))
            Cert(join("tests", "data", "classes.dex"))
Exemple #2
0
    def setUpClass(cls):
        cls.certs = {}

        for filename in listdir(join("tests", "data")):
            if filename in cls.cert_properties:
                cls.certs[filename] = Cert(join("tests", "data", filename),
                                           filename)
    def _extract_and_set_entries(self, string_processing: bool):
        """
        Extract the APK package entries (e.g. AndroidManifest.xml, CERT.RSA, classes.dex, ...) and
        set the corresponding attributes.

        :param string_processing: If True (default), the URLs and shell commands in the classes.dex will be extracted.
        :return: If one of the APK entries is invalid.
        :raise APKParsingError:
        """
        exists_invalid_entry = False

        apk_filepath = self.get_file_path()
        with ZipFile(apk_filepath) as apk:
            tmpdir = tempfile.mkdtemp(APK._TEMPORARY_DIR)

            for filename in apk.namelist():
                entry_filepath = apk.extract(filename, tmpdir)
                self.logger.debug("Extracting APK resource %s to %s", filename, entry_filepath)

                try:
                    if AndroidManifest.looks_like_a_manifest(filename):
                        self.logger.debug("%s looks like a manifest", filename)
                        self._manifest = AndroidManifest(entry_filepath, True, apk_filepath)
                    elif Cert.looks_like_a_cert(filename):
                        self.logger.debug("%s looks like a certificate", filename)
                        self._cert = Cert(entry_filepath, filename)
                    elif Dex.looks_like_a_dex(filename):
                        self.logger.debug("%s looks like a DEX", filename)
                        self._dex_files.append(Dex(entry_filepath, filename, string_processing))
                    else:
                        self.logger.debug("%s looks like a general resource", filename)
                        if not os.path.isdir(entry_filepath):
                            self._files.append(File(entry_filepath, filename))
                except (ParsingError, AndroidManifestParsingError, CertParsingError):
                    exists_invalid_entry = True

            try:
                shutil.rmtree(tmpdir)
            except OSError:
                pass

            if exists_invalid_entry:
                raise APKParsingError
Exemple #4
0
    def test_apk_as_dict(self):
        apk = APK(
            filename="any-apk-file-name",
            size=10,
            md5hash="any-apk-file-md5",
            sha1hash="any-apk-file-sha1",
            sha256hash="any-apk-file-sha256",
            sha512hash="any-apk-file-sha512",
            app_name="any-app-name",
            cert=Cert(
                filename="any-cert-file-name",
                size=20,
                md5hash="any-cert-file-md5",
                sha1hash="any-cert-file-sha1",
                sha256hash="any-cert-file-sha256",
                sha512hash="any-cert-file-sha512",
                serial_number="any-cert-serial-number",
                validity=CertValidity(
                    valid_from="any-cert-validity-from",
                    valid_to="any-cert-validity-to"
                ),
                fingerprint=CertFingerprint(
                    md5="any-cert-fingerprint-md5",
                    sha1="any-cert-fingerprint-sha1",
                    sha256="any-cert-fingerprint-sha256",
                    signature="any-cert-fingerprint-signature",
                    version="any-cert-fingerprint-version"
                ),
                owner=CertParticipant(
                    name="any-cert-owner-name",
                    email="any-cert-owner-email",
                    unit="any-cert-owner-unit",
                    organization="any-cert-owner-organization",
                    city="any-cert-owner-city",
                    state="any-cert-owner-state",
                    country="any-cert-owner-country",
                    domain="any-cert-owner-domain"
                ),
                issuer=CertParticipant(
                    name="any-cert-issuer-name",
                    email="any-cert-issuer-email",
                    unit="any-cert-issuer-unit",
                    organization="any-cert-issuer-organization",
                    city="any-cert-issuer-city",
                    state="any-cert-issuer-state",
                    country="any-cert-issuer-country",
                    domain="any-cert-issuer-domain"
                )
            ),
            manifest=AndroidManifest(
                filename="any-manifest-file-name",
                size=30,
                md5hash="any-manifest-file-md5",
                sha1hash="any-manifest-file-sha1",
                sha256hash="any-manifest-file-sha256",
                sha512hash="any-manifest-file-sha512",
                package_name="any-package-name",
                version=AppVersion(code=1, name="any-version-name"),
                sdk=AppSdk(min_version="10", target_version="15", max_version="20"),
                permissions=[],
                activities=[],
                services=[],
                receivers=[]
            ),
            dex_files=[
                Dex(
                    filename="any-dex-file-name",
                    size=40,
                    md5hash="any-dex-file-md5",
                    sha1hash="any-dex-file-sha1",
                    sha256hash="any-dex-file-sha256",
                    sha512hash="any-dex-file-sha512",
                    strings=[],
                    urls=[],
                    shell_commands=[],
                    custom_signatures=[]
                )
            ],
            other_files=[
                any_file(
                    filename="any-resource-file-name",
                    size=50,
                    md5="any-resource-file-md5",
                    sha1="any-resource-file-sha1",
                    sha256="any-resource-file-sha256",
                    sha512="any-resource-file-sha512"
                )
            ]
        )

        result = apk.as_dict()

        self.assertEqual(
            {
                "file": "any-apk-file-name",
                "size": 10,
                "md5": "any-apk-file-md5",
                "sha1": "any-apk-file-sha1",
                "sha256": "any-apk-file-sha256",
                "sha512": "any-apk-file-sha512",
                "name": "any-app-name",
                "cert": {
                    "file": "any-cert-file-name",
                    "size": 20,
                    "md5": "any-cert-file-md5",
                    "sha1": "any-cert-file-sha1",
                    "sha256": "any-cert-file-sha256",
                    "sha512": "any-cert-file-sha512",
                    "serial_number": "any-cert-serial-number",
                    "validity": {
                        "from": "any-cert-validity-from",
                        "until": "any-cert-validity-to"
                    },
                    "fingerprint": {
                        "md5": "any-cert-fingerprint-md5",
                        "sha1": "any-cert-fingerprint-sha1",
                        "sha256": "any-cert-fingerprint-sha256",
                        "signature": "any-cert-fingerprint-signature",
                        "version": "any-cert-fingerprint-version"
                    },
                    "owner": {
                        "name": "any-cert-owner-name",
                        "email": "any-cert-owner-email",
                        "unit": "any-cert-owner-unit",
                        "organization": "any-cert-owner-organization",
                        "city": "any-cert-owner-city",
                        "state": "any-cert-owner-state",
                        "country": "any-cert-owner-country",
                        "domain": "any-cert-owner-domain"
                    },
                    "issuer": {
                        "name": "any-cert-issuer-name",
                        "email": "any-cert-issuer-email",
                        "unit": "any-cert-issuer-unit",
                        "organization": "any-cert-issuer-organization",
                        "city": "any-cert-issuer-city",
                        "state": "any-cert-issuer-state",
                        "country": "any-cert-issuer-country",
                        "domain": "any-cert-issuer-domain"
                    }
                },
                "manifest": {
                    "file": "any-manifest-file-name",
                    "size": 30,
                    "md5": "any-manifest-file-md5",
                    "sha1": "any-manifest-file-sha1",
                    "sha256": "any-manifest-file-sha256",
                    "sha512": "any-manifest-file-sha512",
                    "package": "any-package-name",
                    "version": {
                        "code": 1,
                        "name": "any-version-name"
                    },
                    "sdk": {
                        "min": "10",
                        "target": "15",
                        "max": "20"
                    },
                    "permissions": []
                },
                "dex": [
                    {
                        "file": "any-dex-file-name",
                        "size": 40,
                        "md5": "any-dex-file-md5",
                        "sha1": "any-dex-file-sha1",
                        "sha256": "any-dex-file-sha256",
                        "sha512": "any-dex-file-sha512",
                        "strings": [],
                        "urls": [],
                        "shell_commands": []
                    }
                ],
                "other": [
                    {
                        "file": "any-resource-file-name",
                        "size": 50,
                        "md5": "any-resource-file-md5",
                        "sha1": "any-resource-file-sha1",
                        "sha256": "any-resource-file-sha256",
                        "sha512": "any-resource-file-sha512",
                    }
                ]
            },
            result
        )
Exemple #5
0
    def test_cert_as_dict(self):
        cert = Cert(
            filename="any-file-name",
            size=10,
            md5hash="any-file-md5",
            sha1hash="any-file-sha1",
            sha256hash="any-file-sha256",
            sha512hash="any-file-sha512",
            serial_number="any-serial-number",
            validity=CertValidity(
                valid_from="any-validity-from",
                valid_to="any-validity-to"
            ),
            fingerprint=CertFingerprint(
                md5="any-fingerprint-md5",
                sha1="any-fingerprint-sha1",
                sha256="any-fingerprint-sha256",
                signature="any-fingerprint-signature",
                version="any-fingerprint-version"
            ),
            owner=CertParticipant(
                name="any-owner-name",
                email="any-owner-email",
                unit="any-owner-unit",
                organization="any-owner-organization",
                city="any-owner-city",
                state="any-owner-state",
                country="any-owner-country",
                domain="any-owner-domain"
            ),
            issuer=CertParticipant(
                name="any-issuer-name",
                email="any-issuer-email",
                unit="any-issuer-unit",
                organization="any-issuer-organization",
                city="any-issuer-city",
                state="any-issuer-state",
                country="any-issuer-country",
                domain="any-issuer-domain"
            )
        )

        result = cert.as_dict()

        self.assertEqual(
            {
                "file": "any-file-name",
                "size": 10,
                "md5": "any-file-md5",
                "sha1": "any-file-sha1",
                "sha256": "any-file-sha256",
                "sha512": "any-file-sha512",
                "serial_number": "any-serial-number",
                "validity": {
                    "from": "any-validity-from",
                    "until": "any-validity-to"
                },
                "fingerprint": {
                    "md5": "any-fingerprint-md5",
                    "sha1": "any-fingerprint-sha1",
                    "sha256": "any-fingerprint-sha256",
                    "signature": "any-fingerprint-signature",
                    "version": "any-fingerprint-version"
                },
                "owner": {
                    "name": "any-owner-name",
                    "email": "any-owner-email",
                    "unit": "any-owner-unit",
                    "organization": "any-owner-organization",
                    "city": "any-owner-city",
                    "state": "any-owner-state",
                    "country": "any-owner-country",
                    "domain": "any-owner-domain"
                },
                "issuer": {
                    "name": "any-issuer-name",
                    "email": "any-issuer-email",
                    "unit": "any-issuer-unit",
                    "organization": "any-issuer-organization",
                    "city": "any-issuer-city",
                    "state": "any-issuer-state",
                    "country": "any-issuer-country",
                    "domain": "any-issuer-domain"
                }
            },
            result
        )
class APK(File, APKInterface):
    """
    Parser implementation for Android APK package.
    """

    _TEMPORARY_DIR = ".ninjadroid"

    def __init__(self, filepath: str, string_processing: bool = True, logger: Logger = logger):
        super(APK, self).__init__(filepath)

        self.logger = logger

        if not self.looks_like_an_apk(filepath):
            raise APKParsingError

        self._files = []  # type: List
        self._dex_files = []  # type: List[Dex]
        self._extract_and_set_entries(string_processing)

        if len(self._files) == 0 or self._cert is None:
            raise APKParsingError

        self._app_name = Aapt.get_app_name(filepath)

    def _extract_and_set_entries(self, string_processing: bool):
        """
        Extract the APK package entries (e.g. AndroidManifest.xml, CERT.RSA, classes.dex, ...) and
        set the corresponding attributes.

        :param string_processing: If True (default), the URLs and shell commands in the classes.dex will be extracted.
        :return: If one of the APK entries is invalid.
        :raise APKParsingError:
        """
        exists_invalid_entry = False

        apk_filepath = self.get_file_path()
        with ZipFile(apk_filepath) as apk:
            tmpdir = tempfile.mkdtemp(APK._TEMPORARY_DIR)

            for filename in apk.namelist():
                entry_filepath = apk.extract(filename, tmpdir)
                self.logger.debug("Extracting APK resource %s to %s", filename, entry_filepath)

                try:
                    if AndroidManifest.looks_like_a_manifest(filename):
                        self.logger.debug("%s looks like a manifest", filename)
                        self._manifest = AndroidManifest(entry_filepath, True, apk_filepath)
                    elif Cert.looks_like_a_cert(filename):
                        self.logger.debug("%s looks like a certificate", filename)
                        self._cert = Cert(entry_filepath, filename)
                    elif Dex.looks_like_a_dex(filename):
                        self.logger.debug("%s looks like a DEX", filename)
                        self._dex_files.append(Dex(entry_filepath, filename, string_processing))
                    else:
                        self.logger.debug("%s looks like a general resource", filename)
                        if not os.path.isdir(entry_filepath):
                            self._files.append(File(entry_filepath, filename))
                except (ParsingError, AndroidManifestParsingError, CertParsingError):
                    exists_invalid_entry = True

            try:
                shutil.rmtree(tmpdir)
            except OSError:
                pass

            if exists_invalid_entry:
                raise APKParsingError

    @staticmethod
    def looks_like_an_apk(filepath: str) -> bool:
        return File.is_a_file(filepath) and is_zipfile(filepath)

    def dump(self) -> Dict:
        dump = super(APK, self).dump()
        dump["app_name"] = self._app_name
        dump["cert"] = self._cert.dump() if self._cert is not None else None
        dump["manifest"] = self._manifest.dump() if self._manifest is not None else None
        dump["dex_files"] = [dex.dump() for dex in self._dex_files]
        dump["other_files"] = []
        for file in self._files:
            dump["other_files"].append(file.dump())
        return dump

    def get_file_list(self) -> List:
        return self._files

    def get_manifest(self) -> AndroidManifest:
        return self._manifest

    def get_cert(self) -> Cert:
        return self._cert

    def get_dex_files(self) -> Sequence[Dex]:
        return self._dex_files

    def get_app_name(self) -> str:
        return self._app_name