Ejemplo n.º 1
0
    def parse(self, filepath: str, filename: str = "") -> Cert:
        """
        :param filepath: path of the CERT file
        :param filename: name of the CERT file
        :return: the parsed CERT file
        :raise: FileParsingError if cannot parse the file
        :raise: CertParsingError if cannot parse the file as a CERT
        """
        self.logger.debug(
            "Parsing CERT file: filepath=\"%s\", filename=\"%s\"", filepath,
            filename)
        file = FileParser(self.logger).parse(filepath, filename)
        raw = self.parse_cert(filepath)

        return Cert(filename=file.get_file_name(),
                    size=file.get_size(),
                    md5hash=file.get_md5(),
                    sha1hash=file.get_sha1(),
                    sha256hash=file.get_sha256(),
                    sha512hash=file.get_sha512(),
                    serial_number=self.__parse_string(
                        raw, pattern=r"^Serial number: (.*)$"),
                    validity=self.parse_validity(raw),
                    fingerprint=self.parse_fingerprint(raw),
                    owner=self.parse_participant(raw,
                                                 pattern=r"^Owner: (.*)$"),
                    issuer=self.parse_participant(raw,
                                                  pattern=r"^Issuer: (.*)$"))
Ejemplo n.º 2
0
 def parse(
         self,
         filepath: str,
         binary: bool = False,
         apk_path: Optional[str] = None,
         extended_processing: bool = True
 ):
     """
     :param filepath: path of the AndroidManifest.xml file
     :param binary: (optional) whether the AndroidManifest.xml file is in binary format or not. False by default.
     :param apk_path: (optional) path of the APK package containing this AndroidManifest.xml file. None by default.
     :param extended_processing: (optional) whether should parse all information or only a summary. True by default.
     :return: the parsed AndroidManifest.xml file
     :raise: FileParsingError if cannot parse the file
     :raise: AndroidManifestParsingError if cannot parse the file as an AndroidManifest.xml
     """
     self.logger.debug("Parsing AndroidManifest.xml file: filepath=\"%s\"", filepath)
     file = FileParser(self.logger).parse(filepath, "AndroidManifest.xml")
     try:
         self.logger.debug("Parsing AndroidManifest.xml from DOM...")
         dom = self.parse_manifest_dom(filepath, binary)
     except AndroidManifestParsingError as error:
         self.logger.debug("Cannot parse AndroidManifest.xml from DOM!")
         if apk_path is None or apk_path == "":
             self.logger.debug("Cannot parse AndroidManifest.xml from APK!")
             raise error
         self.logger.debug("Parsing AndroidManifest.xml from APK: apk_path=%s", apk_path)
         return self.build_manifest_from_apk(file, extended_processing, apk_path)
     return self.build_manifest_from_dom(file, extended_processing, dom)
Ejemplo n.º 3
0
    def parse(self, filepath: str, filename: str) -> Dex:
        """
        :param filepath: path of the dex file
        :param filename: name of the dex file
        :return: the parsed dex file
        :raise: FileParsingError if cannot parse the file
        """
        self.logger.debug("Parsing dex file: filepath=\"%s\", filename=\"%s\"",
                          filepath, filename)
        file = FileParser(self.logger).parse(filepath, filename)

        self.logger.debug("Extracting strings...")
        strings = self.parse_strings(filepath)
        self.logger.debug("Strings extracted: %d", len(strings))

        self.logger.debug("Extracting URLs...")
        urls = self.parse_signatures(signature=UriSignature(),
                                     strings=strings,
                                     min_string_len=6)
        self.logger.debug("URLs extracted: %s ", len(urls))

        self.logger.debug("Extracting shell commands...")
        shell_commands = self.parse_signatures(signature=ShellSignature(),
                                               strings=strings)
        self.logger.debug("Shell commands extracted: %s", len(shell_commands))

        # TODO: improve custom signatures parsing performance (commented in the meanwhile because far too slow)
        # self.logger.debug("Extracting custom signatures...")
        custom_signatures = [
        ]  # self.extract_signatures(signature=Signature(), strings=self._strings)
        # self.logger.debug("Custom signatures extracted: %s", len(custom_signatures))

        return Dex(
            filename=file.get_file_name(),
            size=file.get_size(),
            md5hash=file.get_md5(),
            sha1hash=file.get_sha1(),
            sha256hash=file.get_sha256(),
            sha512hash=file.get_sha512(),
            strings=strings,
            urls=urls,
            shell_commands=shell_commands,
            custom_signatures=custom_signatures,
        )
Ejemplo n.º 4
0
 def __init__(self, logger: Logger = default_logger):
     self.logger = logger
     self.file_parser = FileParser(logger)
     self.manifest_parser = AndroidManifestParser(logger)
     self.cert_parser = CertParser(logger)
     self.dex_parser = DexParser(logger)
Ejemplo n.º 5
0
class TestFileParser(unittest.TestCase):
    """
    Test File parser.
    """

    ANY_FILE_SIZE = 70058
    ANY_FILE_MD5 = "c9504f487c8b51412ba4980bfe3cc15d"
    ANY_FILE_SHA1 = "482a28812495b996a92191fbb3be1376193ca59b"
    ANY_FILE_SHA256 = "8773441a656b60c5e18481fd5ba9c1bf350d98789b975987cb3b2b57ee44ee51"
    ANY_FILE_SHA512 = "559eab9840ff2f8507842605e60bb0730442ddf9ee7ca4ab4f386f715c1a4707766065d6f0b977816886692bf88b400643979e2fd13e6999358a21cabdfb3071"

    sut = FileParser()

    @patch('ninjadroid.parsers.file.sha512')
    @patch('ninjadroid.parsers.file.sha256')
    @patch('ninjadroid.parsers.file.sha1')
    @patch('ninjadroid.parsers.file.md5')
    @patch('ninjadroid.parsers.file.getsize')
    @patch('ninjadroid.parsers.file.access')
    @patch('ninjadroid.parsers.file.isfile')
    @patch("builtins.open", new_callable=mock_open)
    def test_parse(
            self,
            mock_file,
            mock_isfile,
            mock_access,
            mock_getsize,
            mock_md5,
            mock_sha1,
            mock_sha256,
            mock_sha512
    ):
        mock_isfile.return_value = True
        mock_access.return_value = True
        mock_getsize.return_value = self.ANY_FILE_SIZE
        mock_md5.return_value.hexdigest.return_value = self.ANY_FILE_MD5
        mock_sha1.return_value.hexdigest.return_value = self.ANY_FILE_SHA1
        mock_sha256.return_value.hexdigest.return_value = self.ANY_FILE_SHA256
        mock_sha512.return_value.hexdigest.return_value = self.ANY_FILE_SHA512

        file = self.sut.parse("any-file-path", "any-file-name")

        mock_file.assert_called_with("any-file-path", "rb")
        assert_file_equal(
            self,
            expected=any_file(
                filename="any-file-name",
                size=self.ANY_FILE_SIZE,
                md5=self.ANY_FILE_MD5,
                sha1=self.ANY_FILE_SHA1,
                sha256=self.ANY_FILE_SHA256,
                sha512=self.ANY_FILE_SHA512
            ),
            actual=file
        )

    @patch('ninjadroid.parsers.file.sha512')
    @patch('ninjadroid.parsers.file.sha256')
    @patch('ninjadroid.parsers.file.sha1')
    @patch('ninjadroid.parsers.file.md5')
    @patch('ninjadroid.parsers.file.getsize')
    @patch('ninjadroid.parsers.file.access')
    @patch('ninjadroid.parsers.file.isfile')
    @patch("builtins.open", new_callable=mock_open)
    def test_parse_without_filename(
            self,
            mock_file,
            mock_isfile,
            mock_access,
            mock_getsize,
            mock_md5,
            mock_sha1,
            mock_sha256,
            mock_sha512
    ):
        mock_isfile.return_value = True
        mock_access.return_value = True
        mock_getsize.return_value = self.ANY_FILE_SIZE
        mock_md5.return_value.hexdigest.return_value = self.ANY_FILE_MD5
        mock_sha1.return_value.hexdigest.return_value = self.ANY_FILE_SHA1
        mock_sha256.return_value.hexdigest.return_value = self.ANY_FILE_SHA256
        mock_sha512.return_value.hexdigest.return_value = self.ANY_FILE_SHA512

        file = self.sut.parse("any-file-path")

        mock_file.assert_called_with("any-file-path", "rb")
        assert_file_equal(
            self,
            expected=any_file(
                filename="any-file-path",
                size=self.ANY_FILE_SIZE,
                md5=self.ANY_FILE_MD5,
                sha1=self.ANY_FILE_SHA1,
                sha256=self.ANY_FILE_SHA256,
                sha512=self.ANY_FILE_SHA512
            ),
            actual=file
        )

    @patch('ninjadroid.parsers.file.access')
    @patch('ninjadroid.parsers.file.isfile')
    @patch("builtins.open", new_callable=mock_open)
    def test_parse_fails_when_open_fails(self, mock_file, mock_isfile, mock_access):
        mock_isfile.return_value = True
        mock_access.return_value = True
        mock_file.side_effect = OSError()

        with self.assertRaises(OSError):
            self.sut.parse("any-file-path")

    @patch('ninjadroid.parsers.file.access')
    @patch('ninjadroid.parsers.file.isfile')
    def test_parse_fails_with_non_existing_file(self, mock_isfile, mock_access):
        mock_isfile.return_value = False
        mock_access.return_value = True

        with self.assertRaises(FileParsingError):
            self.sut.parse("any-file-path")

    @patch('ninjadroid.parsers.file.access')
    @patch('ninjadroid.parsers.file.isfile')
    def test_parse_fails_with_non_readable_file(self, mock_isfile, mock_access):
        mock_isfile.return_value = True
        mock_access.return_value = False

        with self.assertRaises(FileParsingError):
            self.sut.parse("any-file-path")

    @parameterized.expand([
        [True],
        [False]
    ])
    @patch('ninjadroid.parsers.file.isfile')
    def test_is_file(self, expected, mock_isfile):
        mock_isfile.return_value = expected

        result = self.sut.is_file("any-path")

        self.assertEqual(expected, result)

    def test_is_file_with_empty_path(self):
        result = self.sut.is_file("")

        self.assertFalse(result)

    @parameterized.expand([
        [True, True, True],
        [True, False, False],
        [False, True, False],
        [False, False, False]
    ])
    @patch('ninjadroid.parsers.file.access')
    @patch('ninjadroid.parsers.file.isfile')
    def test_is_readable_file(self, is_file, is_readable, expected, mock_isfile, mock_access):
        mock_isfile.return_value = is_file
        mock_access.return_value = is_readable

        result = self.sut.is_readable_file("any-path")

        self.assertEqual(expected, result)

    def test_is_readable_file_with_empty_path(self):
        result = self.sut.is_readable_file("")

        self.assertFalse(result)

    @parameterized.expand([
        [True, True, True],
        [True, False, False],
        [False, True, False],
        [False, False, False]
    ])
    @patch('ninjadroid.parsers.file.is_zipfile')
    @patch('ninjadroid.parsers.file.isfile')
    def test_is_zip_file(self, is_file, is_zip, expected, mock_isfile, mock_is_zipfile):
        mock_isfile.return_value = is_file
        mock_is_zipfile.return_value = is_zip

        result = self.sut.is_zip_file("any-path")

        self.assertEqual(expected, result)

    def test_is_zip_file_with_empty_path(self):
        result = self.sut.is_zip_file("")

        self.assertFalse(result)

    @parameterized.expand([
        [True],
        [False]
    ])
    @patch('ninjadroid.parsers.file.isdir')
    def test_is_directory(self, expected, mock_isdir):
        mock_isdir.return_value = expected

        result = self.sut.is_directory("any-path")

        self.assertEqual(expected, result)

    def test_is_directory_with_empty_path(self):
        result = self.sut.is_directory("")

        self.assertFalse(result)