Source code for dexray_insight.modules.manifest_analysis

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Android manifest analysis module for intent filters and components."""

# #!/usr/bin/env python3
# # -*- coding: utf-8 -*-
#
# # Copyright (C) {{ year }} Dexray Insight Contributors
# #
# # This file is part of Dexray Insight - Android APK Security Analysis Tool
# #
# # Licensed under the Apache License, Version 2.0 (the "License");
# # you may not use this file except in compliance with the License.
# # You may obtain a copy of the License at
# #
# #     http://www.apache.org/licenses/LICENSE-2.0
# #
# # Unless required by applicable law or agreed to in writing, software
# # distributed under the License is distributed on an "AS IS" BASIS,
# # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# # See the License for the specific language governing permissions and
# # limitations under the License.

import logging
import time
from dataclasses import dataclass
from typing import Any

from ..core.base_classes import AnalysisContext
from ..core.base_classes import AnalysisStatus
from ..core.base_classes import BaseAnalysisModule
from ..core.base_classes import BaseResult
from ..core.base_classes import register_module


@dataclass
class ManifestAnalysisResult(BaseResult):
    """Result class for manifest analysis."""

    package_name: str = ""
    main_activity: str = ""
    permissions: list[str] = None
    activities: list[str] = None
    services: list[str] = None
    receivers: list[str] = None
    content_providers: list[str] = None
    intent_filters: list[dict[str, Any]] = None
    manifest_xml: str = ""

    def __post_init__(self):
        """Initialize default values for optional fields."""
        if self.permissions is None:
            self.permissions = []
        if self.activities is None:
            self.activities = []
        if self.services is None:
            self.services = []
        if self.receivers is None:
            self.receivers = []
        if self.content_providers is None:
            self.content_providers = []
        if self.intent_filters is None:
            self.intent_filters = []

    def to_dict(self) -> dict[str, Any]:
        """Convert result to dictionary."""
        base_dict = super().to_dict()
        base_dict.update(
            {
                "package_name": self.package_name,
                "main_activity": self.main_activity,
                "permissions": self.permissions,
                "activities": self.activities,
                "services": self.services,
                "receivers": self.receivers,
                "content_providers": self.content_providers,
                "intent_filters": self.intent_filters,
                "components_summary": {
                    "total_activities": len(self.activities),
                    "total_services": len(self.services),
                    "total_receivers": len(self.receivers),
                    "total_providers": len(self.content_providers),
                    "total_permissions": len(self.permissions),
                },
            }
        )
        return base_dict


[docs] @register_module("manifest_analysis") class ManifestAnalysisModule(BaseAnalysisModule): """Manifest analysis module for extracting AndroidManifest.xml information."""
[docs] def __init__(self, config: dict[str, Any]): """Initialize ManifestAnalysisModule with configuration.""" super().__init__(config) self.logger = logging.getLogger(__name__) self.extract_intent_filters = config.get("extract_intent_filters", True) self.analyze_exported_components = config.get("analyze_exported_components", True)
[docs] def get_dependencies(self) -> list[str]: """No dependencies for manifest analysis.""" return []
[docs] def analyze(self, apk_path: str, context: AnalysisContext) -> ManifestAnalysisResult: """ Perform manifest analysis on the APK. Args: apk_path: Path to the APK file context: Analysis context Returns: ManifestAnalysisResult with analysis results """ start_time = time.time() try: if not context.androguard_obj: raise ValueError("Androguard object not available in context") apk = context.androguard_obj.get_androguard_apk() # Extract basic information package_name = apk.get_package() or "" main_activity = apk.get_main_activity() or "" permissions = list(apk.get_permissions()) or [] activities = list(apk.get_activities()) or [] services = list(apk.get_services()) or [] receivers = list(apk.get_receivers()) or [] content_providers = list(apk.get_providers()) or [] # Extract intent filters if enabled intent_filters = [] if self.extract_intent_filters: intent_filters = self._extract_intent_filters(apk, services, receivers) # Get manifest XML if needed manifest_xml = "" try: manifest_xml = apk.get_android_manifest_xml() except Exception as e: self.logger.warning(f"Could not extract manifest XML: {str(e)}") execution_time = time.time() - start_time result = ManifestAnalysisResult( module_name=self.name, status=AnalysisStatus.SUCCESS, execution_time=execution_time, package_name=package_name, main_activity=main_activity, permissions=permissions, activities=activities, services=services, receivers=receivers, content_providers=content_providers, intent_filters=intent_filters, manifest_xml=manifest_xml, ) # Check for Mono runtime (for compatibility with existing code) if "mono.MonoRuntimeProvider" in content_providers: # Add runtime information to context for other modules context.add_result("runtime_detected", "dotnetMono") return result except Exception as e: execution_time = time.time() - start_time self.logger.error(f"Manifest analysis failed: {str(e)}") return ManifestAnalysisResult( module_name=self.name, status=AnalysisStatus.FAILURE, execution_time=execution_time, error_message=str(e), )
def _extract_intent_filters(self, apk, services: list[str], receivers: list[str]) -> list[dict[str, Any]]: """Extract intent filters from services and receivers.""" intent_filters = [] try: # Process services for service in services: try: intent_filter = apk.get_intent_filters("service", service) if intent_filter: intent_filters.append( {"component_type": "service", "component_name": service, "filters": intent_filter} ) except Exception as e: self.logger.warning(f"Failed to get intent filters for service {service}: {str(e)}") # Process receivers for receiver in receivers: try: intent_filter = apk.get_intent_filters("receiver", receiver) if intent_filter: intent_filters.append( {"component_type": "receiver", "component_name": receiver, "filters": intent_filter} ) except Exception as e: self.logger.warning(f"Failed to get intent filters for receiver {receiver}: {str(e)}") except Exception as e: self.logger.error(f"Failed to extract intent filters: {str(e)}") return intent_filters
[docs] def validate_config(self) -> bool: """Validate module configuration.""" return True