APK解析基础

APK(Android Package)是Android应用程序的安装包格式,了解APK结构对于App逆向分析至关重要。

APK文件结构详解

# apk_analyzer.py - APK文件结构分析工具
import zipfile
import xml.etree.ElementTree as ET
import os
import json
from typing import Dict, List, Optional
from pathlib import Path
import hashlib

class APKAnalyzer:
    """APK分析器 - 解析APK文件结构"""
    
    def __init__(self, apk_path: str):
        self.apk_path = apk_path
        self.apk_info = {
            'manifest': {},
            'files': [],
            'permissions': [],
            'activities': [],
            'services': [],
            'receivers': [],
            'providers': [],
            'libraries': [],
            'resources': {},
            'metadata': {}
        }
    
    def analyze_apk(self) -> Dict:
        """分析APK文件"""
        print(f"🔍 分析APK文件: {self.apk_path}")
        
        # 检查文件是否存在
        if not os.path.exists(self.apk_path):
            raise FileNotFoundError(f"APK文件不存在: {self.apk_path}")
        
        # 获取文件基本信息
        self._get_file_metadata()
        
        # 解析ZIP结构
        with zipfile.ZipFile(self.apk_path, 'r') as apk:
            file_list = apk.namelist()
            self.apk_info['files'] = file_list
            
            # 分析各组件
            self._analyze_manifest(apk)
            self._analyze_dex_files(apk)
            self._analyze_resources(apk)
            self._analyze_libraries(apk)
        
        return self.apk_info
    
    def _get_file_metadata(self):
        """获取文件元数据"""
        stat = os.stat(self.apk_path)
        self.apk_info['metadata'] = {
            'size': stat.st_size,
            'created': stat.st_ctime,
            'modified': stat.st_mtime,
            'sha256': self._calculate_sha256(),
            'md5': self._calculate_md5()
        }
    
    def _calculate_sha256(self) -> str:
        """计算SHA256哈希值"""
        sha256_hash = hashlib.sha256()
        with open(self.apk_path, "rb") as f:
            for byte_block in iter(lambda: f.read(4096), b""):
                sha256_hash.update(byte_block)
        return sha256_hash.hexdigest()
    
    def _calculate_md5(self) -> str:
        """计算MD5哈希值"""
        md5_hash = hashlib.md5()
        with open(self.apk_path, "rb") as f:
            for byte_block in iter(lambda: f.read(4096), b""):
                md5_hash.update(byte_block)
        return md5_hash.hexdigest()
    
    def _analyze_manifest(self, apk_zip: zipfile.ZipFile):
        """分析AndroidManifest.xml"""
        try:
            # 读取AndroidManifest.xml内容
            manifest_content = apk_zip.read('AndroidManifest.xml')
            
            # 解析XML(注意:APK中的AndroidManifest.xml是二进制格式,需要特殊处理)
            # 这里使用简化方法,实际需要使用axmlparser等工具
            print("📄 解析AndroidManifest.xml...")
            
            # 模拟解析结果
            self.apk_info['manifest'] = {
                'package_name': 'com.example.app',
                'version_code': '1',
                'version_name': '1.0',
                'min_sdk': '21',
                'target_sdk': '30',
                'application_label': 'Example App'
            }
            
            # 解析权限
            self.apk_info['permissions'] = [
                'android.permission.INTERNET',
                'android.permission.WRITE_EXTERNAL_STORAGE',
                'android.permission.ACCESS_NETWORK_STATE'
            ]
            
            # 解析Activities
            self.apk_info['activities'] = [
                {
                    'name': '.MainActivity',
                    'exported': 'true',
                    'launch_mode': 'standard'
                },
                {
                    'name': '.SplashActivity',
                    'exported': 'false',
                    'launch_mode': 'singleTop'
                }
            ]
            
            # 解析Services
            self.apk_info['services'] = [
                {
                    'name': '.BackgroundService',
                    'exported': 'false'
                }
            ]
            
            # 解析Receivers
            self.apk_info['receivers'] = [
                {
                    'name': '.BootReceiver',
                    'exported': 'false'
                }
            ]
            
            # 解析Content Providers
            self.apk_info['providers'] = [
                {
                    'name': '.DataProvider',
                    'exported': 'false',
                    'authorities': 'com.example.provider'
                }
            ]
            
        except KeyError:
            print("⚠️  AndroidManifest.xml 未找到")
        except Exception as e:
            print(f"❌ 解析AndroidManifest.xml失败: {e}")
    
    def _analyze_dex_files(self, apk_zip: zipfile.ZipFile):
        """分析DEX文件"""
        dex_files = [f for f in apk_zip.namelist() if f.endswith('.dex')]
        print(f"📱 发现 {len(dex_files)} 个DEX文件")
        
        for dex_file in dex_files:
            try:
                dex_info = {
                    'name': dex_file,
                    'size': len(apk_zip.read(dex_file)),
                    'compressed_size': apk_zip.getinfo(dex_file).compress_size
                }
                self.apk_info['files_info'] = self.apk_info.get('files_info', [])
                self.apk_info['files_info'].append(dex_info)
                
                print(f"  - {dex_file}: {dex_info['size']} bytes")
            except Exception as e:
                print(f"  ❌ 读取 {dex_file} 失败: {e}")
    
    def _analyze_resources(self, apk_zip: zipfile.ZipFile):
        """分析资源文件"""
        resource_dirs = [f for f in apk_zip.namelist() if f.startswith('res/') or f.startswith('assets/')]
        print(f"🎨 发现 {len(resource_dirs)} 个资源文件")
        
        # 按类型分类资源
        drawable_files = [f for f in resource_dirs if 'drawable' in f]
        layout_files = [f for f in resource_dirs if 'layout' in f]
        values_files = [f for f in resource_dirs if 'values' in f]
        
        self.apk_info['resources'] = {
            'drawables': len(drawable_files),
            'layouts': len(layout_files),
            'values': len(values_files),
            'total_assets': len([f for f in apk_zip.namelist() if f.startswith('assets/')])
        }
        
        print(f"  - 图片资源: {len(drawable_files)} 个")
        print(f"  - 布局文件: {len(layout_files)} 个")
        print(f"  - 配置文件: {len(values_files)} 个")
    
    def _analyze_libraries(self, apk_zip: zipfile.ZipFile):
        """分析原生库文件"""
        lib_files = [f for f in apk_zip.namelist() if f.startswith('lib/')]
        print(f"⚙️  发现 {len(lib_files)} 个原生库文件")
        
        architectures = set()
        for lib_file in lib_files:
            # 提取架构信息
            parts = lib_file.split('/')
            if len(parts) > 1:
                arch = parts[1]  # lib/arm64-v8a/...
                architectures.add(arch)
        
        self.apk_info['libraries'] = {
            'count': len(lib_files),
            'architectures': list(architectures),
            'files': lib_files[:10]  # 只显示前10个
        }
        
        print(f"  - 支持架构: {', '.join(architectures)}")
    
    def generate_report(self) -> str:
        """生成分析报告"""
        report = f"""
=== APK分析报告 ===

📁 文件信息:
- 文件路径: {self.apk_path}
- 文件大小: {self.apk_info['metadata']['size']} bytes
- SHA256: {self.apk_info['metadata']['sha256']}
- MD5: {self.apk_info['metadata']['md5']}

📱 应用信息:
- 包名: {self.apk_info['manifest'].get('package_name', 'N/A')}
- 版本代码: {self.apk_info['manifest'].get('version_code', 'N/A')}
- 版本名称: {self.apk_info['manifest'].get('version_name', 'N/A')}
- 最小SDK: {self.apk_info['manifest'].get('min_sdk', 'N/A')}
- 目标SDK: {self.apk_info['manifest'].get('target_sdk', 'N/A')}

🔐 权限列表 ({len(self.apk_info['permissions'])}个):
"""
        for perm in self.apk_info['permissions']:
            report += f"- {perm}\n"
        
        report += f"\n📱 Activities ({len(self.apk_info['activities'])}个):\n"
        for activity in self.apk_info['activities']:
            report += f"- {activity['name']} (exported: {activity['exported']})\n"
        
        report += f"\n⚙️  Services ({len(self.apk_info['services'])}个):\n"
        for service in self.apk_info['services']:
            report += f"- {service['name']}\n"
        
        report += f"\n📡 Receivers ({len(self.apk_info['receivers'])}个):\n"
        for receiver in self.apk_info['receivers']:
            report += f"- {receiver['name']}\n"
        
        report += f"\n📚 Providers ({len(self.apk_info['providers'])}个):\n"
        for provider in self.apk_info['providers']:
            report += f"- {provider['name']}\n"
        
        report += f"\n🎨 资源统计:\n"
        resources = self.apk_info['resources']
        report += f"- 图片资源: {resources.get('drawables', 0)}\n"
        report += f"- 布局文件: {resources.get('layouts', 0)}\n"
        report += f"- 配置文件: {resources.get('values', 0)}\n"
        report += f"- 资源总数: {resources.get('total_assets', 0)}\n"
        
        report += f"\n⚙️  原生库:\n"
        libs = self.apk_info['libraries']
        report += f"- 库文件数: {libs['count']}\n"
        report += f"- 支持架构: {', '.join(libs['architectures'])}\n"
        
        report += f"\n📄 文件总数: {len(self.apk_info['files'])}"
        
        return report

def analyze_apk_file(apk_path: str) -> Dict:
    """分析APK文件的便捷函数"""
    analyzer = APKAnalyzer(apk_path)
    return analyzer.analyze_apk()

def main():
    """主函数示例"""
    # 注意:这里只是示例,实际使用时需要提供真实的APK文件路径
    print("APK分析工具示例")
    print("请提供APK文件路径进行分析")

if __name__ == "__main__":
    main()

APK反编译工具使用

# apk_decompiler.py - APK反编译工具集成
import subprocess
import os
import tempfile
from typing import Optional, Dict
import shutil

class APKDecompiler:
    """APK反编译工具类"""
    
    def __init__(self):
        self.tools_available = self._check_tools()
    
    def _check_tools(self) -> Dict[str, bool]:
        """检查反编译工具是否可用"""
        tools = {
            'jadx': False,
            'apktool': False,
            'dex2jar': False
        }
        
        # 检查jadx
        try:
            result = subprocess.run(['jadx', '--version'], 
                                  capture_output=True, text=True, timeout=10)
            tools['jadx'] = result.returncode == 0
        except (subprocess.TimeoutExpired, FileNotFoundError):
            tools['jadx'] = False
        
        # 检查apktool
        try:
            result = subprocess.run(['apktool', '--version'], 
                                  capture_output=True, text=True, timeout=10)
            tools['apktool'] = result.returncode == 0
        except (subprocess.TimeoutExpired, FileNotFoundError):
            tools['apktool'] = False
        
        # 检查dex2jar (检查d2j-dex2jar.sh或.bat)
        try:
            result = subprocess.run(['d2j-dex2jar', '--version'], 
                                  capture_output=True, text=True, timeout=10)
            tools['dex2jar'] = result.returncode == 0
        except (subprocess.TimeoutExpired, FileNotFoundError):
            tools['dex2jar'] = False
        
        print("🔍 检测到的反编译工具:")
        for tool, available in tools.items():
            status = "✅ 可用" if available else "❌ 不可用"
            print(f"  {tool}: {status}")
        
        return tools
    
    def decompile_with_jadx(self, apk_path: str, output_dir: str = None) -> Optional[str]:
        """使用jadx反编译APK"""
        if not self.tools_available['jadx']:
            print("❌ jadx工具不可用")
            return None
        
        if not output_dir:
            output_dir = f"jadx_output_{os.path.basename(apk_path).replace('.apk', '')}"
        
        try:
            cmd = ['jadx', '-d', output_dir, apk_path]
            print(f" jadx反编译命令: {' '.join(cmd)}")
            
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
            
            if result.returncode == 0:
                print(f"✅ jadx反编译成功,输出目录: {output_dir}")
                return output_dir
            else:
                print(f"❌ jadx反编译失败: {result.stderr}")
                return None
                
        except subprocess.TimeoutExpired:
            print("❌ jadx反编译超时")
            return None
        except Exception as e:
            print(f"❌ jadx反编译异常: {e}")
            return None
    
    def decompile_with_apktool(self, apk_path: str, output_dir: str = None) -> Optional[str]:
        """使用apktool反编译APK"""
        if not self.tools_available['apktool']:
            print("❌ apktool工具不可用")
            return None
        
        if not output_dir:
            output_dir = f"apktool_output_{os.path.basename(apk_path).replace('.apk', '')}"
        
        try:
            cmd = ['apktool', 'd', apk_path, '-o', output_dir]
            print(f" apktool反编译命令: {' '.join(cmd)}")
            
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
            
            if result.returncode == 0:
                print(f"✅ apktool反编译成功,输出目录: {output_dir}")
                return output_dir
            else:
                print(f"❌ apktool反编译失败: {result.stderr}")
                return None
                
        except subprocess.TimeoutExpired:
            print("❌ apktool反编译超时")
            return None
        except Exception as e:
            print(f"❌ apktool反编译异常: {e}")
            return None
    
    def extract_dex_with_dex2jar(self, apk_path: str, output_dir: str = None) -> Optional[str]:
        """使用dex2jar提取DEX文件"""
        if not self.tools_available['dex2jar']:
            print("❌ dex2jar工具不可用")
            return None
        
        if not output_dir:
            output_dir = f"dex2jar_output_{os.path.basename(apk_path).replace('.apk', '')}"
        
        os.makedirs(output_dir, exist_ok=True)
        
        try:
            # 首先解压APK获取DEX文件
            with tempfile.TemporaryDirectory() as temp_dir:
                # 解压APK
                import zipfile
                with zipfile.ZipFile(apk_path, 'r') as apk:
                    dex_files = [f for f in apk.namelist() if f.endswith('.dex')]
                    for dex_file in dex_files:
                        apk.extract(dex_file, temp_dir)
                
                # 转换DEX到JAR
                jar_files = []
                for dex_file in dex_files:
                    dex_path = os.path.join(temp_dir, dex_file)
                    jar_name = dex_file.replace('.dex', '.jar')
                    jar_path = os.path.join(output_dir, jar_name)
                    
                    cmd = ['d2j-dex2jar', dex_path, '-o', jar_path]
                    print(f" dex2jar转换命令: {' '.join(cmd)}")
                    
                    result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
                    
                    if result.returncode == 0:
                        jar_files.append(jar_path)
                        print(f"✅ {dex_file} -> {jar_name} 转换成功")
                    else:
                        print(f"❌ DEX转换失败: {result.stderr}")
                
                return output_dir if jar_files else None
                
        except Exception as e:
            print(f"❌ dex2jar转换异常: {e}")
            return None
    
    def comprehensive_decompile(self, apk_path: str, output_base_dir: str = "decompiled_apk") -> Dict[str, str]:
        """综合反编译(使用所有可用工具)"""
        results = {}
        
        os.makedirs(output_base_dir, exist_ok=True)
        
        # 使用jadx反编译
        jadx_output = self.decompile_with_jadx(
            apk_path, 
            os.path.join(output_base_dir, "jadx_output")
        )
        if jadx_output:
            results['jadx'] = jadx_output
        
        # 使用apktool反编译
        apktool_output = self.decompile_with_apktool(
            apk_path,
            os.path.join(output_base_dir, "apktool_output")
        )
        if apktool_output:
            results['apktool'] = apktool_output
        
        # 使用dex2jar提取DEX
        dex2jar_output = self.extract_dex_with_dex2jar(
            apk_path,
            os.path.join(output_base_dir, "dex2jar_output")
        )
        if dex2jar_output:
            results['dex2jar'] = dex2jar_output
        
        print(f"\n🎯 综合反编译完成,结果目录:")
        for tool, output_dir in results.items():
            print(f"  {tool}: {output_dir}")
        
        return results

def setup_decompilation_environment():
    """设置反编译环境的说明"""
    setup_guide = """
    === APK反编译环境设置指南 ===
    
    1. 安装Java (jadx和dex2jar需要Java环境)
       - 下载并安装JDK 8+
       - 设置JAVA_HOME环境变量
    
    2. 安装jadx (推荐的APK反编译工具)
       - 下载地址: https://github.com/skylot/jadx
       - 解压后将bin目录添加到PATH环境变量
       - 验证: jadx --version
    
    3. 安装apktool
       - 下载地址: https://ibotpeaches.github.io/Apktool/
       - 下载apktool脚本和jar文件
       - 设置为可执行并添加到PATH
       - 验证: apktool --version
    
    4. 安装dex2jar
       - 下载地址: https://github.com/pxb1988/dex2jar
       - 解压后将目录添加到PATH
       - 验证: d2j-dex2jar --version
    
    5. 安装Python依赖
       pip install lief  # 用于分析ELF文件
       pip install androguard  # 专业的Android恶意软件分析工具
    """
    print(setup_guide)
    return setup_guide

def demo_apk_analysis():
    """演示APK分析流程"""
    print("🔍 APK分析演示")
    
    # 显示可用工具
    decompiler = APKDecompiler()
    
    # 注意:实际使用时需要提供真实的APK文件路径
    print("\nℹ️  要进行实际分析,请提供APK文件路径")
    print("示例: decompiler.comprehensive_decompile('path/to/app.apk')")
    
    # 显示设置指南
    setup_decompilation_environment()

if __name__ == "__main__":
    demo_apk_analysis()

APK安全检测

# apk_security_scanner.py - APK安全扫描工具
from typing import Dict, List, Any
import zipfile
import re
from apk_analyzer import APKAnalyzer

class APKSecurityScanner:
    """APK安全扫描器"""
    
    def __init__(self, apk_path: str):
        self.apk_path = apk_path
        self.analyzer = APKAnalyzer(apk_path)
        self.security_issues = []
    
    def scan_permissions(self) -> List[Dict[str, Any]]:
        """扫描敏感权限"""
        issues = []
        
        # 高危权限列表
        dangerous_permissions = [
            'android.permission.CAMERA',
            'android.permission.RECORD_AUDIO',
            'android.permission.ACCESS_FINE_LOCATION',
            'android.permission.READ_CONTACTS',
            'android.permission.READ_SMS',
            'android.permission.SEND_SMS',
            'android.permission.READ_PHONE_STATE',
            'android.permission.GET_ACCOUNTS',
            'android.permission.READ_CALENDAR',
            'android.permission.WRITE_CALENDAR',
            'android.permission.READ_CALL_LOG',
            'android.permission.WRITE_CALL_LOG',
            'android.permission.ADD_VOICEMAIL',
            'android.permission.USE_SIP',
            'android.permission.PROCESS_OUTGOING_CALLS',
            'android.permission.BODY_SENSORS',
            'android.permission.ACTIVITY_RECOGNITION'
        ]
        
        # 检查manifest中的权限
        apk_info = self.analyzer.analyze_apk()
        permissions = apk_info.get('permissions', [])
        
        for perm in permissions:
            if perm in dangerous_permissions:
                issues.append({
                    'type': 'dangerous_permission',
                    'severity': 'high',
                    'description': f'应用请求高危权限: {perm}',
                    'recommendation': '检查是否真的需要此权限,考虑使用替代方案'
                })
        
        return issues
    
    def scan_network_security_config(self) -> List[Dict[str, Any]]:
        """扫描网络安全配置"""
        issues = []
        
        # 检查是否允许明文流量
        with zipfile.ZipFile(self.apk_path, 'r') as apk:
            # 检查网络安全性配置文件
            ns_config_paths = [
                'res/xml/network_security_config.xml',
                'AndroidManifest.xml'
            ]
            
            for path in ns_config_paths:
                try:
                    content = apk.read(path).decode('utf-8', errors='ignore')
                    
                    # 检查是否允许明文流量
                    if re.search(r'<base-config.*?cleartextTrafficPermitted="true"', content):
                        issues.append({
                            'type': 'insecure_network_config',
                            'severity': 'medium',
                            'description': '应用允许明文HTTP流量,存在安全风险',
                            'recommendation': '配置网络安全策略,禁止明文流量'
                        })
                    
                    # 检查是否信任用户证书
                    if re.search(r'<trust-anchors>.*?<certificates\s+src="user"', content, re.DOTALL):
                        issues.append({
                            'type': 'certificate_validation_bypass',
                            'severity': 'high',
                            'description': '应用信任用户安装的证书,易受中间人攻击',
                            'recommendation': '只信任系统预装证书'
                        })
                        
                except KeyError:
                    continue
                except Exception:
                    continue
        
        return issues
    
    def scan_debug_flags(self) -> List[Dict[str, Any]]:
        """扫描调试标志"""
        issues = []
        
        # 检查AndroidManifest.xml中的调试标志
        with zipfile.ZipFile(self.apk_path, 'r') as apk:
            try:
                manifest_content = apk.read('AndroidManifest.xml').decode('utf-8', errors='ignore')
                
                # 检查是否允许调试
                if re.search(r'android:debuggable="true"', manifest_content):
                    issues.append({
                        'type': 'debug_enabled',
                        'severity': 'high',
                        'description': '应用启用了调试模式,存在安全风险',
                        'recommendation': '发布版本应禁用调试模式'
                    })
                
                # 检查是否允许备份
                if re.search(r'android:allowBackup="true"', manifest_content):
                    issues.append({
                        'type': 'backup_enabled',
                        'severity': 'medium',
                        'description': '应用允许备份,可能泄露敏感数据',
                        'recommendation': '敏感应用应禁用备份功能'
                    })
                    
            except KeyError:
                pass
            except Exception:
                pass
        
        return issues
    
    def scan_code_quality(self) -> List[Dict[str, Any]]:
        """扫描代码质量问题"""
        issues = []
        
        # 检查硬编码的敏感信息
        with zipfile.ZipFile(self.apk_path, 'r') as apk:
            # 搜索所有文本文件
            text_files = [f for f in apk.namelist() if f.endswith(('.xml', '.txt', '.properties'))]
            
            sensitive_patterns = [
                (r'api[_-]?key.*?["\'][A-Za-z0-9_-]{20,}', 'hardcoded_api_key'),
                (r'password.*?["\'][^"\']{6,}', 'hardcoded_password'),
                (r'token.*?["\'][A-Za-z0-9_-]{20,}', 'hardcoded_token'),
                (r'["\']https?://[^"\']*secret[^"\']*["\']', 'hardcoded_secret_url')
            ]
            
            for file_path in text_files:
                try:
                    content = apk.read(file_path).decode('utf-8', errors='ignore')
                    
                    for pattern, issue_type in sensitive_patterns:
                        matches = re.findall(pattern, content, re.IGNORECASE)
                        for match in matches:
                            issues.append({
                                'type': issue_type,
                                'severity': 'high',
                                'description': f'在 {file_path} 中发现硬编码的敏感信息: {match[:50]}...',
                                'recommendation': '使用安全的存储方式,如Android Keystore'
                            })
                            
                except Exception:
                    continue
        
        return issues
    
    def run_full_scan(self) -> Dict[str, Any]:
        """运行完整安全扫描"""
        print(f"🔍 开始扫描APK: {self.apk_path}")
        
        all_issues = []
        
        # 执行各项扫描
        all_issues.extend(self.scan_permissions())
        all_issues.extend(self.scan_network_security_config())
        all_issues.extend(self.scan_debug_flags())
        all_issues.extend(self.scan_code_quality())
        
        # 按严重性排序
        severity_order = {'critical': 0, 'high': 1, 'medium': 2, 'low': 3}
        all_issues.sort(key=lambda x: severity_order.get(x['severity'], 99))
        
        # 生成扫描报告
        report = {
            'applied': self.apk_path,
            'scan_time': '2024-01-01T00:00:00Z',  # 实际应用中应使用当前时间
            'total_issues': len(all_issues),
            'issues_by_severity': {
                'critical': len([i for i in all_issues if i['severity'] == 'critical']),
                'high': len([i for i in all_issues if i['severity'] == 'high']),
                'medium': len([i for i in all_issues if i['severity'] == 'medium']),
                'low': len([i for i in all_issues if i['severity'] == 'low'])
            },
            'issues': all_issues
        }
        
        return report
    
    def print_scan_report(self, report: Dict[str, Any]):
        """打印扫描报告"""
        print(f"\n🛡️  APK安全扫描报告")
        print(f"📁 应用: {report['applied']}")
        print(f"📊 总问题数: {report['total_issues']}")
        print(f"   - 高危: {report['issues_by_severity']['high']}")
        print(f"   - 中危: {report['issues_by_severity']['medium']}")
        print(f"   - 低危: {report['issues_by_severity']['low']}")
        
        if report['issues']:
            print(f"\n🔍 发现的安全问题:")
            for i, issue in enumerate(report['issues'], 1):
                severity_symbol = {
                    'critical': '🔴',
                    'high': '🟠',
                    'medium': '🟡',
                    'low': '🟢'
                }.get(issue['severity'], '⚪')
                
                print(f"\n{i}. {severity_symbol} {issue['type']}")
                print(f"   描述: {issue['description']}")
                print(f"   建议: {issue['recommendation']}")

def scan_apk_security(apk_path: str):
    """扫描APK安全性的便捷函数"""
    scanner = APKSecurityScanner(apk_path)
    report = scanner.run_full_scan()
    scanner.print_scan_report(report)
    return report

def main():
    """主函数示例"""
    print("APK安全扫描工具")
    print("请提供APK文件路径进行安全扫描")

if __name__ == "__main__":
    main()