Source code for dexray_insight.modules.permission_analysis

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import time
import logging
from typing import List, Dict, Any
from dataclasses import dataclass
from pathlib import Path

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

try:
    from androguard.core.bytecodes.apk import APK
except ImportError:
    from androguard.core.apk import APK

@dataclass
class PermissionAnalysisResult(BaseResult):
    """Result class for permission analysis"""
    all_permissions: List[str] = None
    critical_permissions: List[str] = None
    permissions_used: int = 0
    critical_permissions_found: int = 0
    
    def __post_init__(self):
        if self.all_permissions is None:
            self.all_permissions = []
        if self.critical_permissions is None:
            self.critical_permissions = []
    
    def to_dict(self) -> Dict[str, Any]:
        base_dict = super().to_dict()
        base_dict.update({
            'all_permissions': self.all_permissions,
            'critical_permissions': self.critical_permissions,
            'permissions_used': self.permissions_used,
            'critical_permissions_found': self.critical_permissions_found
        })
        return base_dict

[docs] @register_module('permission_analysis') class PermissionAnalysisModule(BaseAnalysisModule): """Permission analysis module for detecting critical Android permissions""" # Default critical permissions list DEFAULT_CRITICAL_PERMISSIONS = [ "SEND_SMS", "SEND_SMS_NO_CONFIRMATION", "CALL_PHONE", "RECEIVE_SMS", "RECEIVE_MMS", "READ_SMS", "WRITE_SMS", "RECEIVE_WAP_PUSH", "READ_CONTACTS", "WRITE_CONTACTS", "READ_PROFILE", "WRITE_PROFILE", "READ_CALENDAR", "WRITE_CALENDAR", "READ_USER_DICTIONARY", "READ_HISTORY_BOOKMARKS", "WRITE_HISTORY_BOOKMARKS", "ACCESS_FINE_LOCATION", "ACCESS_COARSE_LOCATION", "ACCESS_MOCK_LOCATION", "USE_SIP", "GET_ACCOUNTS", "AUTHENTICATE_ACCOUNTS", "USE_CREDENTIALS", "MANAGE_ACCOUNTS", "RECORD_AUDIO", "CAMERA", "PROCESS_OUTGOING_CALLS", "READ_PHONE_STATE", "WRITE_EXTERNAL_STORAGE", "READ_EXTERNAL_STORAGE", "WRITE_SETTINGS", "GET_TASKS", "SYSTEM_ALERT_WINDOW", "SET_ANIMATION_SCALE", "PERSISTENT_ACTIVITY", "MOUNT_UNMOUNT_FILESYSTEMS", "MOUNT_FORMAT_FILESYSTEMS", "WRITE_APN_SETTINGS", "SUBSCRIBED_FEEDS_WRITE", "READ_LOGS", "SET_DEBUG_APP", "SET_PROCESS_LIMIT", "SET_ALWAYS_FINISH", "SIGNAL_PERSISTENT_PROCESSES", "REQUEST_INSTALL_PACKAGES", "ADD_VOICEMAIL", "ACCEPT_HANDOVER", "ANSWER_PHONE_CALLS", "BODY_SENSORS", "READ_CALL_LOG", "READ_PHONE_NUMBERS", "WRITE_CALL_LOG", "ACCESS_BACKGROUND_LOCATION", "ACCESS_MEDIA_LOCATION", "ACTIVITY_RECOGNITION", "MANAGE_EXTERNAL_STORAGE", "READ_PRECISE_PHONE_STATE", "BLUETOOTH_ADVERTISE", "BLUETOOTH_CONNECT", "BLUETOOTH_SCAN", "BODY_SENSORS_BACKGROUND", "NEARBY_WIFI_DEVICES", "POST_NOTIFICATIONS", "READ_MEDIA_AUDIO", "READ_MEDIA_IMAGES", "READ_MEDIA_VIDEO", "READ_MEDIA_VISUAL_USER_SELECTED", "UWB_RANGING" ]
[docs] def __init__(self, config: Dict[str, Any]): super().__init__(config) self.logger = logging.getLogger(__name__) self.critical_permissions_file = config.get('critical_permissions_file') self.use_default_list = config.get('use_default_critical_list', True) self.critical_permissions = self._load_critical_permissions()
[docs] def get_dependencies(self) -> List[str]: """No dependencies for permission analysis""" return []
def _load_critical_permissions(self) -> List[str]: """Load critical permissions from file or use default list""" if self.critical_permissions_file: try: path = Path(self.critical_permissions_file) if path.exists(): with open(path, 'r') as f: content = f.read().strip() # Support both line-separated and comma-separated formats if ',' in content: permissions = [p.strip() for p in content.split(',')] else: permissions = content.split() self.logger.info(f"Loaded {len(permissions)} critical permissions from {self.critical_permissions_file}") return permissions else: self.logger.warning(f"Critical permissions file not found: {self.critical_permissions_file}") except Exception as e: self.logger.error(f"Failed to load critical permissions file: {str(e)}") if self.use_default_list: self.logger.info(f"Using default critical permissions list with {len(self.DEFAULT_CRITICAL_PERMISSIONS)} permissions") return self.DEFAULT_CRITICAL_PERMISSIONS return []
[docs] def analyze(self, apk_path: str, context: AnalysisContext) -> PermissionAnalysisResult: """ Perform permission analysis on the APK Args: apk_path: Path to the APK file context: Analysis context Returns: PermissionAnalysisResult with analysis results """ start_time = time.time() try: # Use existing androguard object if available if context.androguard_obj: # Try to get permissions from the existing androguard object # This would need to be adapted based on the actual interface all_permissions = [] # Placeholder - would extract from androguard_obj else: # Create new APK instance apk = APK(apk_path) all_permissions = apk.get_permissions() # Find critical permissions found_critical_permissions = [] for permission in all_permissions: for critical_permission in self.critical_permissions: if critical_permission in permission: found_critical_permissions.append(permission) break execution_time = time.time() - start_time return PermissionAnalysisResult( module_name=self.name, status=AnalysisStatus.SUCCESS, execution_time=execution_time, all_permissions=all_permissions, critical_permissions=found_critical_permissions, permissions_used=len(all_permissions), critical_permissions_found=len(found_critical_permissions) ) except Exception as e: execution_time = time.time() - start_time self.logger.error(f"Permission analysis failed: {str(e)}") return PermissionAnalysisResult( module_name=self.name, status=AnalysisStatus.FAILURE, execution_time=execution_time, error_message=str(e), all_permissions=[], critical_permissions=[], permissions_used=0, critical_permissions_found=0 )
[docs] def validate_config(self) -> bool: """Validate module configuration""" if self.critical_permissions_file: path = Path(self.critical_permissions_file) if not path.exists(): self.logger.warning(f"Critical permissions file does not exist: {self.critical_permissions_file}") if not self.critical_permissions: self.logger.warning("No critical permissions loaded") return False return True