App逆向初探

课程目标

  • 了解APK文件结构和组成
  • 掌握APK反编译工具的使用方法
  • 学会使用Frida进行Hook操作
  • 掌握绕过SSL Pinning和代理检测的方法
  • 了解App安全检测机制

1. APK解析基础

1.1 APK文件结构

APK (Android Package) 是Android应用程序的安装包文件,本质上是一个ZIP压缩文件。让我们深入了解其内部结构。

# APK文件结构详解
APK文件解压后的主要目录结构:
├── AndroidManifest.xml          # 应用配置文件(二进制格式)
├── classes.dex                  # Dalvik字节码文件(Java代码)
├── assets/                      # 应用资源文件
├── lib/                         # 原生库文件(so文件)
├── res/                         # 应用资源(布局、图片等)
├── resources.arsc               # 编译后的资源文件
├── META-INF/                    # 签名信息
   ├── MANIFEST.MF
   ├── CERT.SF
   └── CERT.RSA
└── original/                    # 原始MANIFEST.MF(可选)

# 常见的DEX文件(多个)
├── classes.dex
├── classes2.dex
├── classes3.dex
# ...

APK结构分析工具

import zipfile
import os
from typing import Dict, List
import xml.etree.ElementTree as ET

class APKAnalyzer:
    """APK分析器"""
    
    def __init__(self, apk_path: str):
        self.apk_path = apk_path
        self.apk_zip = zipfile.ZipFile(apk_path, 'r')
        self.files = self.apk_zip.namelist()
    
    def get_apk_structure(self) -> Dict[str, List[str]]:
        """获取APK文件结构"""
        structure = {}
        for file_path in self.files:
            directory = os.path.dirname(file_path)
            if directory not in structure:
                structure[directory] = []
            structure[directory].append(os.path.basename(file_path))
        
        return structure
    
    def get_manifest_info(self) -> Dict:
        """获取应用清单信息"""
        manifest_data = self.apk_zip.read('AndroidManifest.xml')
        
        # 由于AndroidManifest.xml是二进制格式,需要特殊处理
        # 这里使用androguard库进行解析(需要先安装:pip install androguard)
        try:
            from androguard.core.bytecodes.apk import APK
            apk = APK(self.apk_path)
            
            info = {
                'package': apk.get_package(),
                'version_name': apk.get_androidversion_name(),
                'version_code': apk.get_androidversion_code(),
                'min_sdk': apk.get_min_sdk_version(),
                'target_sdk': apk.get_target_sdk_version(),
                'permissions': apk.get_permissions(),
                'activities': apk.get_activities(),
                'services': apk.get_services(),
                'receivers': apk.get_receivers(),
                'providers': apk.get_providers(),
            }
            return info
        except ImportError:
            print("androguard库未安装,无法解析AndroidManifest.xml")
            return {}
    
    def find_dex_files(self) -> List[str]:
        """查找DEX文件"""
        dex_files = []
        for file_path in self.files:
            if file_path.endswith('.dex'):
                dex_files.append(file_path)
        return dex_files
    
    def find_native_libs(self) -> Dict[str, List[str]]:
        """查找原生库文件"""
        libs = {}
        for file_path in self.files:
            if file_path.startswith('lib/') and file_path.endswith('.so'):
                abi = file_path.split('/')[1]  # ABI类型 (arm64-v8a, armeabi-v7a, x86等)
                if abi not in libs:
                    libs[abi] = []
                libs[abi].append(file_path)
        return libs
    
    def find_assets(self) -> List[str]:
        """查找assets目录下的文件"""
        assets = []
        for file_path in self.files:
            if file_path.startswith('assets/'):
                assets.append(file_path)
        return assets
    
    def find_resources(self) -> List[str]:
        """查找res目录下的资源文件"""
        resources = []
        for file_path in self.files:
            if file_path.startswith('res/'):
                resources.append(file_path)
        return resources
    
    def get_signature_info(self) -> Dict:
        """获取签名信息"""
        signature_info = {}
        for file_path in self.files:
            if file_path.startswith('META-INF/'):
                if file_path.endswith(('.MF', '.SF', '.RSA')):
                    signature_info[file_path] = len(self.apk_zip.read(file_path))
        return signature_info
    
    def scan_for_sensitive_info(self) -> Dict[str, List[str]]:
        """扫描敏感信息"""
        sensitive_patterns = {
            'api_keys': [r'api[_-]?key', r'x-api-key', r'authorization'],
            'urls': [r'https?://', r'ftp://'],
            'secrets': [r'password', r'pwd', r'secret', r'token', r'key'],
            'crypto': [r'cipher', r'aes', r'rsa', r'encrypt', r'decrypt']
        }
        
        findings = {key: [] for key in sensitive_patterns.keys()}
        
        # 搜索DEX文件中的敏感信息
        for dex_file in self.find_dex_files():
            dex_content = self.apk_zip.read(dex_file)
            try:
                dex_text = dex_content.decode('utf-8', errors='ignore')
                for category, patterns in sensitive_patterns.items():
                    for pattern in patterns:
                        import re
                        matches = re.findall(pattern, dex_text, re.IGNORECASE)
                        findings[category].extend(matches)
            except:
                continue
        
        # 搜索assets文件中的敏感信息
        for asset_file in self.find_assets():
            asset_content = self.apk_zip.read(asset_file)
            try:
                asset_text = asset_content.decode('utf-8', errors='ignore')
                for category, patterns in sensitive_patterns.items():
                    for pattern in patterns:
                        import re
                        matches = re.findall(pattern, asset_text, re.IGNORECASE)
                        findings[category].extend(matches)
            except:
                continue
        
        return findings
    
    def close(self):
        """关闭APK文件"""
        self.apk_zip.close()

# 使用示例
def analyze_apk(apk_path: str):
    """分析APK文件"""
    analyzer = APKAnalyzer(apk_path)
    
    print("=== APK结构分析 ===")
    structure = analyzer.get_apk_structure()
    for directory, files in structure.items():
        print(f"{directory}/: {len(files)} files")
        if len(files) <= 5:  # 只显示文件数较少的目录内容
            for file in files:
                print(f"  - {file}")
    
    print("\n=== DEX文件 ===")
    dex_files = analyzer.find_dex_files()
    for dex_file in dex_files:
        print(f"  - {dex_file}")
    
    print("\n=== 原生库文件 ===")
    native_libs = analyzer.find_native_libs()
    for abi, libs in native_libs.items():
        print(f"  {abi}:")
        for lib in libs:
            print(f"    - {lib}")
    
    print("\n=== 敏感信息扫描 ===")
    sensitive_info = analyzer.scan_for_sensitive_info()
    for category, findings in sensitive_info.items():
        if findings:
            print(f"  {category}: {findings[:10]}...")  # 只显示前10个发现
    
    analyzer.close()

# 高级APK分析
class AdvancedAPKAnalyzer(APKAnalyzer):
    """高级APK分析器"""
    
    def __init__(self, apk_path: str):
        super().__init__(apk_path)
        self.decoded_dir = None
    
    def decode_apk(self, output_dir: str = None) -> str:
        """使用apktool解码APK"""
        import subprocess
        import tempfile
        
        if output_dir is None:
            self.decoded_dir = tempfile.mkdtemp(prefix='apk_decode_')
        else:
            self.decoded_dir = output_dir
        
        try:
            # 使用apktool解码
            cmd = ['java', '-jar', 'apktool.jar', 'd', self.apk_path, '-o', self.decoded_dir]
            result = subprocess.run(cmd, capture_output=True, text=True)
            
            if result.returncode == 0:
                print(f"APK解码成功: {self.decoded_dir}")
                return self.decoded_dir
            else:
                print(f"APK解码失败: {result.stderr}")
                return None
        except FileNotFoundError:
            print("apktool未找到,请确保已安装Java和apktool")
            return None
    
    def find_encrypted_strings(self) -> List[Dict[str, str]]:
        """查找可能加密的字符串"""
        encrypted_strings = []
        
        # 搜索常见的加密/编码模式
        encoding_patterns = [
            r'[A-Za-z0-9+/]{20,}={0,2}',  # Base64
            r'[0-9a-fA-F]{32,}',          # MD5/SHA等哈希
            r'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}',  # UUID
        ]
        
        for dex_file in self.find_dex_files():
            content = self.apk_zip.read(dex_file)
            try:
                text_content = content.decode('utf-8', errors='ignore')
                for i, pattern in enumerate(encoding_patterns):
                    import re
                    matches = re.findall(pattern, text_content)
                    for match in matches:
                        encrypted_strings.append({
                            'pattern_type': ['Base64', 'Hash', 'UUID'][i],
                            'match': match,
                            'dex_file': dex_file,
                            'context': text_content[max(0, text_content.find(match)-50):text_content.find(match)+len(match)+50]
                        })
            except:
                continue
        
        return encrypted_strings
    
    def analyze_network_communication(self) -> Dict[str, List[str]]:
        """分析网络通信相关代码"""
        network_analysis = {
            'http_urls': [],
            'api_endpoints': [],
            'network_libraries': [],
            'certificate_pins': []
        }
        
        for dex_file in self.find_dex_files():
            content = self.apk_zip.read(dex_file)
            try:
                text_content = content.decode('utf-8', errors='ignore')
                
                # 查找HTTP/HTTPS URL
                import re
                http_urls = re.findall(r'https?://[^\s\'\"<>\)]+', text_content)
                network_analysis['http_urls'].extend(http_urls)
                
                # 查找API端点
                api_patterns = [
                    r'/api/v\d+/[a-zA-Z0-9/_-]+',
                    r'/rest/[a-zA-Z0-9/_-]+',
                    r'/service/[a-zA-Z0-9/_-]+'
                ]
                for pattern in api_patterns:
                    matches = re.findall(pattern, text_content)
                    network_analysis['api_endpoints'].extend(matches)
                
                # 查找网络库使用
                network_libs = re.findall(r'okhttp|retrofit|volley|httpclient|HttpURLConnection', text_content, re.IGNORECASE)
                network_analysis['network_libraries'].extend(network_libs)
                
                # 查找证书固定相关代码
                pinning_patterns = [
                    r'sslsocketfactory|trustmanager|certificate|pinning|pin|cert',
                    r'NetworkSecurityConfig|CertificatePinner'
                ]
                for pattern in pinning_patterns:
                    matches = re.findall(pattern, text_content, re.IGNORECASE)
                    network_analysis['certificate_pins'].extend(matches)
                    
            except:
                continue
        
        return network_analysis

def main_analysis():
    """主分析函数"""
    apk_path = "example.apk"  # 替换为实际APK路径
    
    # 基础分析
    basic_analyzer = APKAnalyzer(apk_path)
    basic_info = basic_analyzer.get_apk_structure()
    print("基础分析完成")
    
    # 高级分析
    advanced_analyzer = AdvancedAPKAnalyzer(apk_path)
    network_info = advanced_analyzer.analyze_network_communication()
    encrypted_strings = advanced_analyzer.find_encrypted_strings()
    
    print(f"发现网络URL: {len(network_info['http_urls'])}")
    print(f"发现API端点: {len(network_info['api_endpoints'])}")
    print(f"发现加密字符串: {len(encrypted_strings)}")
    
    basic_analyzer.close()
    advanced_analyzer.close()

if __name__ == "__main__":
    main_analysis()

1.2 反编译三剑客

jadx-gui - DEX反编译神器

# jadx安装和使用
# 下载地址: https://github.com/skylot/jadx
# 或使用命令安装
wget https://github.com/skylot/jadx/releases/download/v1.4.7/jadx-1.4.7.zip
unzip jadx-1.4.7.zip

# 使用jadx反编译APK
./jadx/bin/jadx -d output_dir app.apk

# 命令行模式
./jadx/bin/jadx-cli -d output_dir app.apk

# GUI模式
./jadx/bin/jadx-gui app.apk

dex2jar - DEX转JAR工具

# dex2jar安装和使用
# 下载地址: https://github.com/pxb1988/dex2jar
git clone https://github.com/pxb1988/dex2jar.git

# 将DEX转换为JAR
d2j-dex2jar.sh classes.dex

# 然后可以用JD-GUI查看JAR文件

apktool - 资源文件反编译

# apktool安装
# 下载apktool.jar和包装脚本
wget https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool
wget https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.7.0.jar
mv apktool_2.7.0.jar apktool.jar
chmod +x apktool

# 反编译APK
./apktool d app.apk -o app_decoded

# 重新打包APK
./apktool b app_decoded -o app_modified.apk

# 保持原始资源(不反混淆资源ID)
./apktool d app.apk -r  # 不反编译资源
./apktool d app.apk -s  # 不反编译smali

1.3 代码分析实战

import os
import re
from typing import List, Dict, Tuple
from pathlib import Path

class SmaliAnalyzer:
    """Smali代码分析器"""
    
    def __init__(self, decoded_apk_path: str):
        self.decoded_path = Path(decoded_apk_path)
        self.smali_files = self._find_smali_files()
    
    def _find_smali_files(self) -> List[Path]:
        """查找Smali文件"""
        smali_files = []
        for smali_file in self.decoded_path.rglob("*.smali"):
            smali_files.append(smali_file)
        return smali_files
    
    def find_network_methods(self) -> List[Dict[str, str]]:
        """查找网络相关方法"""
        network_methods = []
        
        network_indicators = [
            'HttpClient', 'HttpURLConnection', 'OkHttpClient', 'Retrofit',
            'Socket', 'URL', 'HttpsURLConnection', 'WebView', 'WebViewClient'
        ]
        
        for smali_file in self.smali_files:
            content = smali_file.read_text(encoding='utf-8', errors='ignore')
            
            for indicator in network_indicators:
                if indicator in content:
                    lines = content.split('\n')
                    for i, line in enumerate(lines):
                        if indicator in line:
                            # 获取方法上下文
                            start_line = max(0, i-5)
                            end_line = min(len(lines), i+6)
                            context = '\n'.join(lines[start_line:end_line])
                            
                            network_methods.append({
                                'file': str(smali_file.relative_to(self.decoded_path)),
                                'indicator': indicator,
                                'line_number': i+1,
                                'context': context,
                                'full_line': line.strip()
                            })
        
        return network_methods
    
    def find_crypto_methods(self) -> List[Dict[str, str]]:
        """查找加密相关方法"""
        crypto_methods = []
        
        crypto_indicators = [
            'Cipher', 'MessageDigest', 'SecretKeySpec', 'KeyGenerator',
            'Mac', 'Signature', 'X509Certificate', 'CertificateFactory',
            'KeyStore', 'SSLContext', 'TrustManager'
        ]
        
        for smali_file in self.smali_files:
            content = smali_file.read_text(encoding='utf-8', errors='ignore')
            
            for indicator in crypto_indicators:
                if indicator in content:
                    lines = content.split('\n')
                    for i, line in enumerate(lines):
                        if indicator in line:
                            start_line = max(0, i-3)
                            end_line = min(len(lines), i+4)
                            context = '\n'.join(lines[start_line:end_line])
                            
                            crypto_methods.append({
                                'file': str(smali_file.relative_to(self.decoded_path)),
                                'indicator': indicator,
                                'line_number': i+1,
                                'context': context,
                                'full_line': line.strip()
                            })
        
        return crypto_methods
    
    def find_string_constants(self) -> List[Dict[str, str]]:
        """查找字符串常量"""
        string_constants = []
        
        for smali_file in self.smali_files:
            content = smali_file.read_text(encoding='utf-8', errors='ignore')
            
            # 查找字符串常量定义
            string_pattern = r'const-string[^"]*"([^"]*)"'
            matches = re.findall(string_pattern, content)
            
            lines = content.split('\n')
            for match in matches:
                for i, line in enumerate(lines):
                    if match in line and 'const-string' in line:
                        start_line = max(0, i-2)
                        end_line = min(len(lines), i+3)
                        context = '\n'.join(lines[start_line:end_line])
                        
                        string_constants.append({
                            'file': str(smali_file.relative_to(self.decoded_path)),
                            'string_value': match,
                            'line_number': i+1,
                            'context': context,
                            'full_line': line.strip()
                        })
        
        return string_constants
    
    def find_intent_filters(self) -> List[Dict[str, str]]:
        """查找Intent Filter配置"""
        intent_filters = []
        
        for smali_file in self.smali_files:
            content = smali_file.read_text(encoding='utf-8', errors='ignore')
            
            # 查找Intent相关配置
            intent_patterns = [
                r'action:(.+)',
                r'category:(.+)',
                r'data:(.+)'
            ]
            
            for pattern in intent_patterns:
                matches = re.findall(pattern, content)
                if matches:
                    intent_filters.append({
                        'file': str(smali_file.relative_to(self.decoded_path)),
                        'type': pattern,
                        'matches': matches
                    })
        
        return intent_filters

class JavaCodeAnalyzer:
    """Java代码分析器(用于反编译后的Java代码)"""
    
    def __init__(self, java_src_path: str):
        self.java_path = Path(java_src_path)
        self.java_files = self._find_java_files()
    
    def _find_java_files(self) -> List[Path]:
        """查找Java文件"""
        java_files = []
        for java_file in self.java_path.rglob("*.java"):
            java_files.append(java_file)
        return java_files
    
    def find_api_calls(self) -> List[Dict[str, str]]:
        """查找API调用"""
        api_calls = []
        
        api_patterns = [
            r'https?://[^\s\'\"<>\)]+',
            r'new URL\([\'"][^\'"]+[\'"]\)',
            r'OkHttpClient\(\)',
            r'Retrofit\.Builder\(\)',
            r'HttpURLConnection',
            r'Socket\(.*"[^"]+",\s*\d+\)'
        ]
        
        for java_file in self.java_files:
            content = java_file.read_text(encoding='utf-8', errors='ignore')
            
            for pattern in api_patterns:
                matches = re.findall(pattern, content)
                if matches:
                    api_calls.append({
                        'file': str(java_file.relative_to(self.java_path)),
                        'pattern': pattern,
                        'matches': matches,
                        'count': len(matches)
                    })
        
        return api_calls
    
    def find_crypto_usage(self) -> List[Dict[str, str]]:
        """查找加密使用"""
        crypto_usage = []
        
        crypto_imports = [
            'javax.crypto',
            'java.security',
            'android.security',
            'org.apache.http.conn.ssl',
            'javax.net.ssl'
        ]
        
        crypto_classes = [
            'Cipher', 'MessageDigest', 'SecretKeySpec', 'KeyGenerator',
            'Mac', 'Signature', 'X509Certificate', 'CertificateFactory',
            'KeyStore', 'SSLContext', 'TrustManager', 'HostnameVerifier'
        ]
        
        for java_file in self.java_files:
            content = java_file.read_text(encoding='utf-8', errors='ignore')
            
            # 检查导入
            imports_found = []
            for imp in crypto_imports:
                if imp in content:
                    imports_found.append(imp)
            
            # 检查类使用
            classes_found = []
            for cls in crypto_classes:
                if cls in content:
                    classes_found.append(cls)
            
            if imports_found or classes_found:
                crypto_usage.append({
                    'file': str(java_file.relative_to(self.java_path)),
                    'imports': imports_found,
                    'classes': classes_found
                })
        
        return crypto_usage
    
    def find_hardcoded_secrets(self) -> List[Dict[str, str]]:
        """查找硬编码的敏感信息"""
        secrets = []
        
        secret_patterns = [
            (r'"[A-Za-z0-9+/]{20,}={0,2}"', 'Base64 String'),
            (r'"[0-9a-fA-F]{32,}"', 'Hex String (likely hash)'),
            (r'"[A-Za-z0-9_-]{20,}"', 'Long Alphanumeric String'),
            (r'"sk_[a-zA-Z0-9]+"', 'Stripe Secret Key'),
            (r'"rk_[a-zA-Z0-9]+"', 'Stripe Publishable Key'),
            (r'"AIza[a-zA-Z0-9_-]+"', 'Google API Key'),
            (r'"[A-Za-z0-9]{32,}"', 'Long Hexadecimal'),
            (r'"[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}"', 'UUID in String'),
        ]
        
        for java_file in self.java_files:
            content = java_file.read_text(encoding='utf-8', errors='ignore')
            
            for pattern, description in secret_patterns:
                matches = re.findall(pattern, content)
                for match in matches:
                    # 获取上下文
                    lines = content.split('\n')
                    for i, line in enumerate(lines):
                        if match.strip('"') in line:
                            start_line = max(0, i-2)
                            end_line = min(len(lines), i+3)
                            context = '\n'.join(lines[start_line:end_line])
                            
                            secrets.append({
                                'file': str(java_file.relative_to(self.java_path)),
                                'type': description,
                                'value': match.strip('"'),
                                'context': context,
                                'line_number': i+1
                            })
        
        return secrets

# 使用示例
def analyze_decoded_apk(decoded_path: str):
    """分析解码后的APK"""
    print(f"分析解码后的APK: {decoded_path}")
    
    # Smali分析
    smali_analyzer = SmaliAnalyzer(decoded_path)
    
    print("\n=== 网络方法分析 ===")
    network_methods = smali_analyzer.find_network_methods()
    for method in network_methods[:10]:  # 只显示前10个
        print(f"文件: {method['file']}")
        print(f"指标: {method['indicator']}")
        print(f"行号: {method['line_number']}")
        print(f"内容: {method['full_line'][:100]}...")
        print("-" * 50)
    
    print(f"\n发现网络相关方法: {len(network_methods)} 个")
    
    print("\n=== 加密方法分析 ===")
    crypto_methods = smali_analyzer.find_crypto_methods()
    print(f"发现加密相关方法: {len(crypto_methods)} 个")
    
    print("\n=== 字符串常量分析 ===")
    string_constants = smali_analyzer.find_string_constants()
    print(f"发现字符串常量: {len(string_constants)} 个")
    
    # Java代码分析(如果有反编译的Java代码)
    java_path = Path(decoded_path)
    if (java_path / "java_src").exists():
        java_analyzer = JavaCodeAnalyzer(java_path / "java_src")
        
        print("\n=== API调用分析 ===")
        api_calls = java_analyzer.find_api_calls()
        print(f"发现API调用: {len(api_calls)} 处")
        
        print("\n=== 敏感信息扫描 ===")
        secrets = java_analyzer.find_hardcoded_secrets()
        for secret in secrets:
            print(f"文件: {secret['file']}")
            print(f"类型: {secret['type']}")
            print(f"值: {secret['value'][:50]}...")
            print(f"上下文: {secret['context'][:100]}...")
            print("-" * 50)

if __name__ == "__main__":
    # 使用方法
    # analyze_decoded_apk("path/to/decoded/apk")
    pass

2. Hook技术之神:Frida

Frida是目前最流行的动态分析工具,它允许我们在运行时注入JavaScript代码到目标进程中。

2.1 Frida基础

# Frida安装
pip install frida-tools

# 检查设备连接
frida-ls-devices

# 启动frida-server(在Android设备上)
adb push frida-server /data/local/tmp/
adb shell "chmod 755 /data/local/tmp/frida-server"
adb shell "/data/local/tmp/frida-server &"

# 基本使用命令
frida-ps -U  # 列出USB设备上的进程
frida-trace -U -i "*open*" com.example.app  # 跟踪open函数调用

Frida Python API

import frida
import sys
import time
from typing import Optional

class FridaHooker:
    """Frida Hook工具类"""
    
    def __init__(self, device_id: str = None):
        self.device = None
        self.session = None
        self.script = None
        self.device_id = device_id
        self.connect_device()
    
    def connect_device(self):
        """连接设备"""
        try:
            if self.device_id:
                self.device = frida.get_usb_device(1)  # 1秒超时
            else:
                self.device = frida.get_usb_device(1)
            print(f"连接到设备: {self.device.name}")
        except Exception as e:
            print(f"连接设备失败: {e}")
            # 尝试连接本地设备
            try:
                self.device = frida.get_local_device()
                print(f"连接到本地设备: {self.device.name}")
            except Exception as e2:
                print(f"连接本地设备也失败: {e2}")
                sys.exit(1)
    
    def attach_to_process(self, process_identifier: str):
        """附加到进程"""
        try:
            # 尝试作为进程名连接
            self.session = self.device.attach(process_identifier)
            print(f"成功附加到进程: {process_identifier}")
            return True
        except frida.ProcessNotFoundError:
            # 如果不是进程名,尝试作为PID连接
            try:
                pid = int(process_identifier)
                self.session = self.device.attach(pid)
                print(f"成功附加到PID: {pid}")
                return True
            except (ValueError, frida.ProcessNotFoundError):
                print(f"无法附加到进程: {process_identifier}")
                return False
    
    def create_script(self, js_code: str) -> Optional[object]:
        """创建脚本"""
        try:
            self.script = self.session.create_script(js_code)
            self.script.load()
            print("脚本加载成功")
            return self.script
        except Exception as e:
            print(f"脚本加载失败: {e}")
            return None
    
    def hook_method(self, class_name: str, method_name: str, replacement_code: str = None):
        """Hook Java方法"""
        if replacement_code is None:
            # 默认的Hook代码
            replacement_code = """
            console.log("[*] Hooking " + className + "." + methodName);
            var clazz = Java.use(className);
            var method = clazz[methodName];
            
            method.implementation = function() {
                console.log("[+] " + className + "." + methodName + " called with:");
                var args = [];
                for (var i = 0; i < arguments.length; i++) {
                    args.push(arguments[i]);
                    console.log("    arg[" + i + "]: " + arguments[i]);
                }
                
                var result = this[methodName].apply(this, arguments);
                console.log("[+] Returned: " + result);
                
                return result;
            };
            """
        
        js_code = f"""
        Java.perform(function() {{
            var className = "{class_name}";
            var methodName = "{method_name}";
            {replacement_code}
        }});
        """
        
        return self.create_script(js_code)
    
    def hook_native_function(self, module_name: str, function_name: str, offset: int = None):
        """Hook原生函数"""
        if offset is not None:
            # 使用偏移量Hook
            js_code = f"""
            var baseAddr = Module.findBaseAddress('{module_name}');
            var funcAddr = baseAddr.add({offset});
            
            Interceptor.attach(funcAddr, {{
                onEnter: function(args) {{
                    console.log('[+] Hooking {module_name}+{offset}');
                    for (var i = 0; i < 5 && i < args.length; i++) {{
                        console.log('    arg[' + i + '] = ' + args[i]);
                    }}
                }},
                onLeave: function(retval) {{
                    console.log('[+] Function returned: ' + retval);
                }}
            }});
            """
        else:
            # 使用函数名Hook
            js_code = f"""
            var funcPtr = Module.findExportByName('{module_name}', '{function_name}');
            if (funcPtr !== null) {{
                Interceptor.attach(funcPtr, {{
                    onEnter: function(args) {{
                        console.log('[+] Hooking {module_name}.{function_name}');
                        for (var i = 0; i < 5 && i < args.length; i++) {{
                            console.log('    arg[' + i + '] = ' + args[i]);
                        }}
                    }},
                    onLeave: function(retval) {{
                        console.log('[+] Function returned: ' + retval);
                    }}
                }});
            }} else {{
                console.log('[-] Could not find function: {module_name}.{function_name}');
            }}
            """
        
        return self.create_script(js_code)
    
    def list_java_classes(self):
        """列出所有Java类"""
        js_code = """
        Java.perform(function() {
            var classes = Java.enumerateLoadedClassesSync();
            console.log("Loaded classes: " + classes.length);
            for (var i = 0; i < Math.min(classes.length, 100); i++) {
                console.log("  " + classes[i]);
            }
        });
        """
        
        script = self.create_script(js_code)
        time.sleep(2)  # 等待执行完成
        return script
    
    def list_native_exports(self, module_name: str):
        """列出原生模块导出函数"""
        js_code = f"""
        var exports = Module.enumerateExportsSync('{module_name}');
        console.log("Exports for {module_name}: " + exports.length);
        exports.forEach(function(exp) {{
            console.log("  " + exp.name + ": " + exp.address);
        }});
        """
        
        script = self.create_script(js_code)
        time.sleep(2)  # 等待执行完成
        return script
    
    def detach(self):
        """分离会话"""
        if self.script:
            self.script.unload()
        if self.session:
            self.session.detach()

# 使用示例
def frida_examples():
    """Frida使用示例"""
    hooker = FridaHooker()
    
    # 附加到某个应用(需要先启动应用)
    if hooker.attach_to_process("com.example.app"):
        
        # Hook网络请求相关方法
        hooker.hook_method("javax.net.ssl.HttpsURLConnection", "getInputStream")
        hooker.hook_method("okhttp3.OkHttpClient", "newCall")
        
        # Hook加密相关方法
        hooker.hook_method("javax.crypto.Cipher", "doFinal")
        hooker.hook_method("java.security.MessageDigest", "digest")
        
        # Hook字符串相关方法(可能捕捉到敏感信息)
        hooker.hook_method("java.lang.String", "<init>")
        
        try:
            print("Hook已设置,按Ctrl+C退出...")
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print("\n正在退出...")
        
        hooker.detach()

if __name__ == "__main__":
    frida_examples()

2.2 Frida高级Hook技巧

class AdvancedFridaHooks:
    """高级Frida Hook技巧"""
    
    def __init__(self, session):
        self.session = session
    
    def hook_ssl_pinning_bypass(self):
        """绕过SSL Pinning"""
        js_code = """
        console.log("[.] Cert Pinning Bypass/Hooking started...");
        
        // OkHttp (squareup)
        var okhttp3Certificates = [
            'okhttp3.CertificatePinner', 
            'okhttp3.CertificatePinner$Builder',
            'okhttp3.Handshake'
        ];
        
        okhttp3Certificates.forEach(function(cert_class) {
            try {
                var certificatePinner = Java.use(cert_class);
                
                if (cert_class === 'okhttp3.CertificatePinner') {
                    certificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {
                        console.log('[+] OkHTTP 3.x CertificatePinner check called for: ' + a);
                        // Don't call the original method - effectively bypassing the check
                    };
                    
                    certificatePinner['check$okhttp'].implementation = function(a, b, c) {
                        console.log('[+] OkHTTP 3.x CertificatePinner check$okhttp called');
                        // Bypass
                    };
                }
            } catch (err) {
                console.log('[-] OkHTTP 3.x pinner not found: ' + err);
            }
        });
        
        // TrustManagerImpl (Android 7+)
        try {
            var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
            
            // Android 7+
            TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, target, host) {
                console.log('[+] Bypassing TrustManagerImpl->verifyChain() for host: ' + host);
                return untrustedChain; // Return the certificates as trusted
            };
        } catch (err) {
            console.log('[-] TrustManagerImpl not found: ' + err);
        }
        
        // Apache HTTP Client
        try {
            var AbstractVerifier = Java.use("ch.boye.httpclientandroidlib.conn.ssl.AbstractVerifier");
            AbstractVerifier.verify.overload('java.lang.String', 'javax.net.ssl.SSLSession').implementation = function() {
                console.log('[+] Bypassing AbstractVerifier->verify()');
                // Do nothing, allow connection
            };
            AbstractVerifier.verify.overload('java.lang.String', 'java.security.cert.X509Certificate').implementation = function() {
                console.log('[+] Bypassing AbstractVerifier->verify()');
                // Do nothing, allow connection
            };
        } catch (err) {
            console.log('[-] Apache HTTP Client verifier not found: ' + err);
        }
        
        // NetworkSecurityConfig (Android 7+)
        try {
            var NetworkSecurityConfig = Java.use("android.security.net.config.NetworkSecurityConfig");
            NetworkSecurityConfig.isPinningEnforced.implementation = function() {
                console.log('[+] Bypassing NetworkSecurityConfig->isPinningEnforced()');
                return false;
            };
        } catch (err) {
            console.log('[-] NetworkSecurityConfig not found: ' + err);
        }
        
        console.log("[+] SSL Pinning Bypass hooks installed.");
        """
        
        script = self.session.create_script(js_code)
        script.load()
        return script
    
    def hook_root_detection_bypass(self):
        """绕过Root检测"""
        js_code = """
        console.log("[.] Root Detection Bypass started...");
        
        // Common root detection files
        var rootFiles = [
            '/system/app/Superuser.apk',
            '/sbin/su',
            '/system/bin/su',
            '/system/xbin/su',
            '/data/local/xbin/su',
            '/data/local/bin/su',
            '/system/sd/xbin/su',
            '/system/bin/failsafe/su',
            '/data/local/magisk',
            '/cache/magisk'
        ];
        
        // Hook file access
        var File = Java.use("java.io.File");
        var originalInit = File.$init.overload('java.lang.String');
        originalInit.implementation = function(path) {
            if (rootFiles.indexOf(path) !== -1) {
                console.log('[+] Blocked access to root file: ' + path);
                // Return a file that doesn't exist
                return originalInit.call(this, '/this/file/does/not/exist');
            }
            return originalInit.call(this, path);
        };
        
        // Hook getRuntime
        var Runtime = Java.use("java.lang.Runtime");
        var originalExec = Runtime.exec.overload('java.lang.String');
        var originalExec2 = Runtime.exec.overload('java.lang.String', '[Ljava.lang.String;');
        var originalExec3 = Runtime.exec.overload('[Ljava.lang.String;');
        
        originalExec.implementation = function(command) {
            if (command === 'su') {
                console.log('[+] Blocked su command: ' + command);
                throw new Java.Lang.Exception("Command blocked");
            }
            console.log('[+] Exec command: ' + command);
            return originalExec.call(this, command);
        };
        
        originalExec2.implementation = function(command, env) {
            if (command === 'su') {
                console.log('[+] Blocked su command: ' + command);
                throw new Java.Lang.Exception("Command blocked");
            }
            console.log('[+] Exec command: ' + command);
            return originalExec2.call(this, command, env);
        };
        
        // Hook Build Tags
        var Build = Java.use("android.os.Build");
        var BuildTags = Build.TAGS.value;
        if (BuildTags.indexOf('test-keys') !== -1) {
            console.log('[+] Modifying BUILD.TAGS to remove test-keys');
            Build.TAGS.value = BuildTags.replace('test-keys', 'release-keys');
        }
        
        // Hook System Properties
        var System = Java.use("java.lang.System");
        var originalGetProperty = System.getProperty.overload('java.lang.String');
        originalGetProperty.implementation = function(property) {
            if (property === 'ro.debuggable' || property === 'ro.secure') {
                console.log('[+] Intercepted system property: ' + property);
                if (property === 'ro.debuggable') return '0';
                if (property === 'ro.secure') return '1';
            }
            return originalGetProperty.call(this, property);
        };
        
        console.log("[+] Root Detection Bypass hooks installed.");
        """
        
        script = self.session.create_script(js_code)
        script.load()
        return script
    
    def hook_sqlite_operations(self):
        """Hook SQLite操作"""
        js_code = """
        console.log("[.] SQLite Operations Hooking started...");
        
        // Hook SQLiteDatabase
        try {
            var SQLiteDatabase = Java.use('android.database.sqlite.SQLiteDatabase');
            
            // Hook rawQuery
            SQLiteDatabase.rawQuery.overload('java.lang.String', '[Ljava.lang.String;').implementation = function(sql, selectionArgs) {
                console.log('[+] SQLiteDatabase.rawQuery called:');
                console.log('    SQL: ' + sql);
                if (selectionArgs) {
                    console.log('    Args: ' + JSON.stringify(selectionArgs));
                }
                
                var result = this.rawQuery(sql, selectionArgs);
                return result;
            };
            
            // Hook execSQL
            SQLiteDatabase.execSQL.overload('java.lang.String').implementation = function(sql) {
                console.log('[+] SQLiteDatabase.execSQL called:');
                console.log('    SQL: ' + sql);
                
                this.execSQL(sql);
            };
            
            SQLiteDatabase.execSQL.overload('java.lang.String', '[Ljava.lang.Object;').implementation = function(sql, bindArgs) {
                console.log('[+] SQLiteDatabase.execSQL called with args:');
                console.log('    SQL: ' + sql);
                if (bindArgs) {
                    console.log('    Args: ' + JSON.stringify(bindArgs));
                }
                
                this.execSQL(sql, bindArgs);
            };
            
            // Hook insert
            SQLiteDatabase.insert.overload('java.lang.String', 'java.lang.String', 'android.content.ContentValues').implementation = function(table, nullColumnHack, values) {
                console.log('[+] SQLiteDatabase.insert called:');
                console.log('    Table: ' + table);
                if (values) {
                    var keySet = values.keySet();
                    var iterator = keySet.iterator();
                    console.log('    Values:');
                    while (iterator.hasNext()) {
                        var key = iterator.next();
                        var value = values.get(key);
                        console.log('        ' + key + ': ' + value);
                    }
                }
                
                var result = this.insert(table, nullColumnHack, values);
                return result;
            };
            
            // Hook update
            SQLiteDatabase.update.overload('java.lang.String', 'android.content.ContentValues', 'java.lang.String', '[Ljava.lang.String;').implementation = function(table, values, whereClause, whereArgs) {
                console.log('[+] SQLiteDatabase.update called:');
                console.log('    Table: ' + table);
                console.log('    Where: ' + whereClause);
                if (whereArgs) {
                    console.log('    Where Args: ' + JSON.stringify(whereArgs));
                }
                if (values) {
                    var keySet = values.keySet();
                    var iterator = keySet.iterator();
                    console.log('    New Values:');
                    while (iterator.hasNext()) {
                        var key = iterator.next();
                        var value = values.get(key);
                        console.log('        ' + key + ': ' + value);
                    }
                }
                
                var result = this.update(table, values, whereClause, whereArgs);
                return result;
            };
            
        } catch (err) {
            console.log('[-] SQLiteDatabase hooks failed: ' + err);
        }
        
        console.log("[+] SQLite Operations hooks installed.");
        """
        
        script = self.session.create_script(js_code)
        script.load()
        return script
    
    def trace_crypto_operations(self):
        """跟踪加密操作"""
        js_code = """
        console.log("[.] Crypto Operations Tracing started...");
        
        // Hook Cipher operations
        try {
            var Cipher = Java.use('javax.crypto.Cipher');
            
            Cipher.init.overload('int', 'java.security.Key').implementation = function(opmode, key) {
                console.log('[+] Cipher.init called:');
                console.log('    Opmode: ' + opmode);
                console.log('    Key: ' + key);
                
                this.init(opmode, key);
            };
            
            Cipher.init.overload('int', 'java.security.Key', 'java.security.spec.AlgorithmParameterSpec').implementation = function(opmode, key, params) {
                console.log('[+] Cipher.init called with params:');
                console.log('    Opmode: ' + opmode);
                console.log('    Key: ' + key);
                console.log('    Params: ' + params);
                
                this.init(opmode, key, params);
            };
            
            Cipher.doFinal.overload('[B').implementation = function(input) {
                console.log('[+] Cipher.doFinal called:');
                console.log('    Input length: ' + input.length);
                
                var result = this.doFinal(input);
                console.log('    Output length: ' + result.length);
                
                return result;
            };
            
            Cipher.doFinal.overload('[B', 'int').implementation = function(input, outputOffset) {
                console.log('[+] Cipher.doFinal called with offset:');
                console.log('    Input length: ' + input.length);
                console.log('    Offset: ' + outputOffset);
                
                var result = this.doFinal(input, outputOffset);
                console.log('    Output length: ' + result.length);
                
                return result;
            };
            
        } catch (err) {
            console.log('[-] Cipher hooks failed: ' + err);
        }
        
        // Hook MessageDigest
        try {
            var MessageDigest = Java.use('java.security.MessageDigest');
            
            MessageDigest.digest.overload().implementation = function() {
                console.log('[+] MessageDigest.digest() called');
                
                var result = this.digest();
                console.log('    Digest length: ' + result.length);
                
                return result;
            };
            
            MessageDigest.digest.overload('[B').implementation = function(input) {
                console.log('[+] MessageDigest.digest(input) called:');
                console.log('    Input length: ' + input.length);
                
                var result = this.digest(input);
                console.log('    Digest length: ' + result.length);
                
                return result;
            };
            
            MessageDigest.update.overload('byte').implementation = function(input) {
                console.log('[+] MessageDigest.update(byte) called:');
                console.log('    Input: ' + input);
                
                this.update(input);
            };
            
            MessageDigest.update.overload('[B').implementation = function(input) {
                console.log('[+] MessageDigest.update(byte[]) called:');
                console.log('    Input length: ' + input.length);
                
                this.update(input);
            };
            
        } catch (err) {
            console.log('[-] MessageDigest hooks failed: ' + err);
        }
        
        console.log("[+] Crypto Operations tracing installed.");
        """
        
        script = self.session.create_script(js_code)
        script.load()
        return script

# 完整的Frida工具类
class CompleteFridaToolkit:
    """完整的Frida工具包"""
    
    def __init__(self):
        self.device = None
        self.session = None
        self.advanced_hooks = None
        self.connect_to_device()
    
    def connect_to_device(self):
        """连接到设备"""
        try:
            self.device = frida.get_usb_device(2)  # 2秒超时
            print(f"✓ 连接到设备: {self.device.name}")
        except:
            try:
                self.device = frida.get_remote_device()
                print(f"✓ 连接到远程设备: {self.device.name}")
            except:
                print("✗ 无法连接到设备")
                return False
        return True
    
    def attach_to_app(self, package_name: str):
        """附加到应用"""
        try:
            # 等待应用启动
            pid = self.device.spawn([package_name])
            self.session = self.device.attach(pid)
            self.device.resume(pid)
            
            print(f"✓ 附加到应用: {package_name}")
            
            # 初始化高级hooks
            self.advanced_hooks = AdvancedFridaHooks(self.session)
            return True
        except Exception as e:
            print(f"✗ 附加失败: {e}")
            return False
    
    def enable_ssl_bypass(self):
        """启用SSL绕过"""
        if self.advanced_hooks:
            self.advanced_hooks.hook_ssl_pinning_bypass()
            print("✓ SSL Pinning 绕过已启用")
    
    def enable_root_bypass(self):
        """启用Root检测绕过"""
        if self.advanced_hooks:
            self.advanced_hooks.hook_root_detection_bypass()
            print("✓ Root 检测绕过已启用")
    
    def enable_sqlite_hook(self):
        """启用SQLite Hook"""
        if self.advanced_hooks:
            self.advanced_hooks.hook_sqlite_operations()
            print("✓ SQLite 操作Hook已启用")
    
    def enable_crypto_trace(self):
        """启用加密操作跟踪"""
        if self.advanced_hooks:
            self.advanced_hooks.trace_crypto_operations()
            print("✓ 加密操作跟踪已启用")
    
    def interactive_hook(self, class_name: str, method_name: str):
        """交互式Hook"""
        js_code = f"""
        Java.perform(function() {{
            var targetClass = Java.use("{class_name}");
            var targetMethod = targetClass.{method_name};
            
            console.log("Setting hook on {class_name}.{method_name}");
            
            targetMethod.implementation = function() {{
                console.log(">>> {class_name}.{method_name} called!");
                
                // 打印参数
                for (var i = 0; i < arguments.length; i++) {{
                    try {{
                        console.log("    Arg[" + i + "]: " + arguments[i]);
                    }} catch(e) {{
                        console.log("    Arg[" + i + "]: [Could not convert to string]");
                    }}
                }}
                
                // 调用原始方法
                var result = this.{method_name}.apply(this, arguments);
                
                try {{
                    console.log("<<< Returned: " + result);
                }} catch(e) {{
                    console.log("<<< Returned: [Could not convert to string]");
                }}
                
                return result;
            }};
        }});
        """
        
        script = self.session.create_script(js_code)
        script.load()
        print(f"✓ 交互式Hook已设置: {class_name}.{method_name}")
        return script

def main_frida_toolkit():
    """主Frida工具函数"""
    toolkit = CompleteFridaToolkit()
    
    # 附加到目标应用
    if toolkit.attach_to_app("com.example.targetapp"):
        
        # 启用各种绕过和Hook
        toolkit.enable_ssl_bypass()
        toolkit.enable_root_bypass()
        toolkit.enable_sqlite_hook()
        toolkit.enable_crypto_trace()
        
        print("\\n所有Hook已设置完成!")
        print("应用现在应该可以绕过常见的安全检测。")
        
        try:
            print("\\n按 Ctrl+C 退出...")
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            print("\\n退出中...")

if __name__ == "__main__":
    main_frida_toolkit()

3. 绕过检测机制

3.1 SSL Pinning绕过

# 除了Frida方法,还有其他绕过SSL Pinning的方式

class SSLPinningBypassMethods:
    """SSL Pinning绕过方法集合"""
    
    @staticmethod
    def patch_apk_ssl_pinning():
        """通过修改APK绕过SSL Pinning"""
        # 这需要解包APK,修改相关代码,然后重新打包
        # 通常涉及修改网络请求相关的类和方法
        pass
    
    @staticmethod
    def network_level_bypass():
        """网络层面绕过"""
        # 使用代理工具如Burp Suite, Charles等配置SSL代理
        # 配合修改设备信任的CA证书
        pass
    
    @staticmethod
    def system_level_modifications():
        """系统级别修改"""
        # 需要ROOT权限,将代理证书安装到系统证书目录
        # 修改系统网络配置
        pass

# Xposed模块绕过(需要ROOT和Xposed框架)
XPOSED_SSL_PINNING_BYPASS_CODE = """
// Xposed模块代码示例
package com.example.sslbypass;

import de.robv.android.xposed.IXposedHookLoadPackage;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.callbacks.XC_LoadPackage.LoadPackageParam;
import static de.robv.android.xposed.XposedHelpers.findAndHookMethod;

public class SSLBypass implements IXposedHookLoadPackage {
    
    public void handleLoadPackage(final LoadPackageParam lpparam) throws Throwable {
        // Hook OkHttp3
        try {
            Class<?> certificatePinnerClass = lpparam.classLoader.loadClass("okhttp3.CertificatePinner");
            findAndHookMethod(certificatePinnerClass, "check", String.class, List.class, new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    // Bypass SSL Pinning
                    param.setResult(null);
                }
            });
        } catch (Exception e) {
            // OkHttp3 not found
        }
        
        // Hook Android Network Security Config
        try {
            Class<?> networkSecurityClass = lpparam.classLoader.loadClass("android.security.net.config.NetworkSecurityConfig");
            findAndHookMethod(networkSecurityClass, "isPinningEnforced", new XC_MethodHook() {
                @Override
                protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
                    param.setResult(false);
                }
            });
        } catch (Exception e) {
            // Network Security Config not found
        }
    }
}
"""

3.2 代理检测绕过

class ProxyDetectionBypass:
    """代理检测绕过技术"""
    
    @staticmethod
    def detect_and_bypass_proxy_checks():
        """检测并绕过代理检查"""
        # Android应用通常检查以下代理设置:
        # 1. 系统代理设置
        # 2. 网络接口状态
        # 3. 特定端口开放情况
        
        proxy_check_methods = [
            # 检查系统代理设置
            "System.getProperty('http.proxyHost')",
            "System.getProperty('https.proxyHost')",
            "System.getProperty('proxyHost')",
            
            # 检查网络接口
            "ConnectivityManager.getActiveNetworkInfo()",
            "NetworkInterface.getNetworkInterfaces()",
            
            # 检查特定端口
            "Socket.connect() to proxy ports"
        ]
        
        return proxy_check_methods
    
    @staticmethod
    def frida_bypass_proxy_detection():
        """使用Frida绕过代理检测"""
        js_code = """
        Java.perform(function() {
            // Hook system properties related to proxy
            var System = Java.use('java.lang.System');
            
            var originalGetProperty = System.getProperty.overload('java.lang.String');
            originalGetProperty.implementation = function(property) {
                if (property.indexOf('proxy') !== -1) {
                    console.log('[+] Intercepted proxy property check: ' + property);
                    // Return empty or fake values
                    if (property === 'http.proxyHost' || property === 'https.proxyHost') {
                        return null; // No proxy configured
                    }
                }
                return originalGetProperty.call(this, property);
            };
            
            // Hook HttpURLConnection to bypass proxy
            var HttpURLConnection = Java.use('java.net.HttpURLConnection');
            var originalGetConnectMethod = HttpURLConnection.getConnectTimeout;
            
            // Hook proxy setting methods
            var Proxy = Java.use('java.net.Proxy');
            var HttpHost = Java.use('org.apache.http.HttpHost');
            
            console.log('[+] Proxy detection bypass hooks installed');
        });
        """
        return js_code
    
    @staticmethod
    def simulate_clean_network_env():
        """模拟干净的网络环境"""
        # 这可以通过修改系统属性和网络接口信息来实现
        pass

# 模拟器检测绕过
class EmulatorDetectionBypass:
    """模拟器检测绕过"""
    
    @staticmethod
    def common_emulator_indicators():
        """常见的模拟器指标"""
        indicators = {
            'hardware': [
                'goldfish',      # Android虚拟设备
                'ranchu',        # 新版Android模拟器
                'vbox86',        # VirtualBox
                'vbox'           # VirtualBox (older)
            ],
            'features': [
                'com.google.android.feature.AUDIO_OUTPUT',  # 模拟器音频特性
                'com.google.android.feature.CAMERA_FLASH',  # 没有闪光灯
            ],
            'properties': {
                'ro.hardware': ['ranchu', 'goldfish'],
                'ro.kernel.qemu': ['1'],  # QEMU标识
                'ro.boot.qemu': ['1'],    # QEMU启动标识
            },
            'files': [
                '/ueventd.android_x86.rc',  # x86模拟器文件
                '/system/bin/winyounker',   # 某些模拟器特有的文件
            ],
            'build_props': {
                'ro.product.model': ['Android SDK built for x86', 'Full Android on x86'],
                'ro.product.brand': ['generic', 'generic_x86'],
                'ro.product.device': ['generic', 'generic_x86'],
            }
        }
        return indicators
    
    @staticmethod
    def frida_bypass_emulator_detection():
        """使用Frida绕过模拟器检测"""
        js_code = """
        Java.perform(function() {
            // Hook Build properties
            var Build = Java.use('android.os.Build');
            
            // Modify hardware identifiers
            if (Build.BOARD.value.indexOf('ranchu') !== -1) {
                Build.BOARD.value = 'UMI';
            }
            if (Build.BOOTLOADER.value.indexOf('unknown') !== -1) {
                Build.BOOTLOADER.value = '0x9DFF'; // Real bootloader value
            }
            if (Build.BRAND.value.indexOf('generic') !== -1) {
                Build.BRAND.value = 'Xiaomi';
            }
            if (Build.DEVICE.value.indexOf('generic') !== -1) {
                Build.DEVICE.value = 'umi';
            }
            if (Build.DISPLAY.value.indexOf('generic') !== -1) {
                Build.DISPLAY.value = 'RKQ1.200826.002';
            }
            if (Build.FINGERPRINT.value.indexOf('generic') !== -1) {
                Build.FINGERPRINT.value = 'Xiaomi/umi/umi:11/RKQ1.200826.002/20.10.22:user/release-keys';
            }
            if (Build.HARDWARE.value.indexOf('ranchu') !== -1) {
                Build.HARDWARE.value = 'qcom';
            }
            if (Build.HOST.value.indexOf('generic') !== -1) {
                Build.HOST.value = 'c5-miui-ota-bd104.bj';
            }
            if (Build.MANUFACTURER.value.indexOf('unknown') !== -1) {
                Build.MANUFACTURER.value = 'Xiaomi';
            }
            if (Build.MODEL.value.indexOf('Android') !== -1 || Build.MODEL.value.indexOf('AOSP') !== -1) {
                Build.MODEL.value = 'Mi 10 Pro';
            }
            if (Build.PRODUCT.value.indexOf('generic') !== -1) {
                Build.PRODUCT.value = 'umi';
            }
            
            // Hook file checks
            var File = Java.use('java.io.File');
            var originalInit = File.$init.overload('java.lang.String');
            originalInit.implementation = function(filepath) {
                var emulatorFiles = [
                    '/ueventd.android_x86.rc',
                    '/system/bin/winyounker',
                    '/sys/class/net/eth1',
                    '/sys/class/net/tun0'
                ];
                
                if (emulatorFiles.indexOf(filepath) !== -1) {
                    // Make it seem like the file doesn't exist
                    filepath = '/this/file/does/not/exist';
                }
                
                return originalInit.call(this, filepath);
            };
            
            // Hook system properties
            var System = Java.use('java.lang.System');
            var originalGetProperty = System.getProperty.overload('java.lang.String');
            originalGetProperty.implementation = function(property) {
                if (property === 'ro.kernel.qemu' || property === 'ro.boot.qemu') {
                    return null; // Return null instead of '1'
                }
                return originalGetProperty.call(this, property);
            };
            
            console.log('[+] Emulator detection bypass hooks installed');
        });
        """
        return js_code

4. 实践项目:某金融App逆向分析

class FinanceAppReverser:
    """金融App逆向分析工具"""
    
    def __init__(self, apk_path: str):
        self.apk_path = apk_path
        self.analyzer = AdvancedAPKAnalyzer(apk_path)
        self.security_features = []
    
    def analyze_security_features(self):
        """分析安全特性"""
        print("=== 分析金融App安全特性 ===")
        
        # 查找加密相关代码
        crypto_analysis = self.analyzer.find_encrypted_strings()
        print(f"发现加密字符串: {len(crypto_analysis)} 个")
        
        # 分析网络通信
        network_analysis = self.analyzer.analyze_network_communication()
        print(f"发现HTTP URL: {len(network_analysis['http_urls'])} 个")
        print(f"发现API端点: {len(network_analysis['api_endpoints'])} 个")
        print(f"使用网络库: {list(set(network_analysis['network_libraries']))}")
        
        # 检查SSL Pinning
        ssl_pinning_indicators = [item for item in network_analysis['certificate_pins'] if 'pin' in item.lower()]
        print(f"SSL Pinning 相关代码: {len(ssl_pinning_indicators)} 处")
        
        # 检查其他安全功能
        security_indicators = {
            'root_detection': [],
            'tamper_detection': [],
            'debug_detection': [],
            'hook_detection': []
        }
        
        # 搜索安全检测相关代码
        for dex_file in self.analyzer.find_dex_files():
            content = self.analyzer.apk_zip.read(dex_file)
            text_content = content.decode('utf-8', errors='ignore')
            
            if 'root' in text_content.lower():
                security_indicators['root_detection'].append(dex_file)
            if 'debug' in text_content.lower():
                security_indicators['debug_detection'].append(dex_file)
            if 'hook' in text_content.lower():
                security_indicators['hook_detection'].append(dex_file)
            if 'tamper' in text_content.lower() or 'integrity' in text_content.lower():
                security_indicators['tamper_detection'].append(dex_file)
        
        self.security_features = security_indicators
        
        print("\\n安全检测功能分析:")
        for feature, locations in security_indicators.items():
            print(f"  {feature}: {len(locations)} 个位置")
        
        return security_indicators
    
    def generate_frida_script(self) -> str:
        """生成针对性的Frida脚本"""
        script_parts = [
            "// 金融App专用Frida绕过脚本",
            "// 根据分析结果生成的绕过代码",
            "",
            "Java.perform(function() {",
        ]
        
        # 添加SSL Pinning绕过
        if self.security_features.get('certificate_pins'):
            script_parts.extend([
                "    // SSL Pinning 绕过",
                "    try {",
                "        var okhttp3CertificatePinner = Java.use('okhttp3.CertificatePinner');",
                "        okhttp3CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(a, b) {",
                "            console.log('[+] OkHTTP3 SSL Pinning bypassed for: ' + a);",
                "            return;",
                "        };",
                "    } catch(e) {",
                "        console.log('[-] OkHTTP3 not found');",
                "    }",
                ""
            ])
        
        # 添加Root检测绕过
        if self.security_features.get('root_detection'):
            script_parts.extend([
                "    // Root 检测绕过",
                "    var fileClass = Java.use('java.io.File');",
                "    var originalInit = fileClass.$init.overload('java.lang.String');",
                "    originalInit.implementation = function(path) {",
                "        var rootFiles = ['/system/app/Superuser.apk', '/sbin/su', '/system/bin/su', '/system/xbin/su'];",
                "        if (rootFiles.indexOf(path) !== -1) {",
                "            console.log('[+] Root check bypassed for: ' + path);",
                "            path = '/nonexistent/file';",
                "        }",
                "        return originalInit.call(this, path);",
                "    };",
                ""
            ])
        
        # 添加调试检测绕过
        if self.security_features.get('debug_detection'):
            script_parts.extend([
                "    // 调试检测绕过",
                "    var debugClass = Java.use('android.os.Debug');",
                "    var isDebuggerConnected = debugClass.isDebuggerConnected;",
                "    isDebuggerConnected.implementation = function() {",
                "        console.log('[+] Debug check bypassed');",
                "        return false;",
                "    };",
                ""
            ])
        
        script_parts.append("});")
        
        return "\\n".join(script_parts)
    
    def export_analysis_report(self, output_path: str):
        """导出分析报告"""
        report = {
            'apk_info': self.analyzer.get_manifest_info(),
            'security_features': self.security_features,
            'network_analysis': self.analyzer.analyze_network_communication(),
            'recommendations': self.generate_recommendations()
        }
        
        import json
        with open(output_path, 'w', encoding='utf-8') as f:
            json.dump(report, f, indent=2, ensure_ascii=False)
        
        print(f"分析报告已导出到: {output_path}")
        return report
    
    def generate_recommendations(self) -> List[str]:
        """生成绕过建议"""
        recommendations = []
        
        if self.security_features.get('certificate_pins'):
            recommendations.append("检测到SSL Pinning,建议使用Frida绕过")
        
        if self.security_features.get('root_detection'):
            recommendations.append("检测到Root检测,需要绕过文件检查")
        
        if self.security_features.get('debug_detection'):
            recommendations.append("检测到调试检测,需要隐藏调试器")
        
        if self.security_features.get('hook_detection'):
            recommendations.append("检测到Hook检测,需要使用隐藏技术")
        
        return recommendations

def main_finance_app_analysis():
    """主金融App分析函数"""
    # 注意:实际使用时需要替换为真实的APK路径
    # app_reverser = FinanceAppReverser("finance_app.apk")
    # 
    # # 分析安全特性
    # security_features = app_reverser.analyze_security_features()
    # 
    # # 生成Frida脚本
    # frida_script = app_reverser.generate_frida_script()
    # print("生成的Frida脚本:")
    # print(frida_script)
    # 
    # # 导出分析报告
    # app_reverser.export_analysis_report("analysis_report.json")
    
    print("金融App逆向分析工具已准备就绪")
    print("使用方法:")
    print("1. 创建FinanceAppReverser实例")
    print("2. 调用analyze_security_features()分析安全特性")
    print("3. 使用generate_frida_script()生成绕过脚本")
    print("4. 导出分析报告")

if __name__ == "__main__":
    main_finance_app_analysis()

5. 本章总结

本章我们深入学习了App逆向技术:

  1. APK解析:掌握了APK文件结构,学会了使用各种反编译工具
  2. Frida技术:深入了解了动态Hook技术,掌握了绕过各种安全检测的方法
  3. 安全绕过:学习了SSL Pinning、Root检测、模拟器检测等绕过技术
  4. 实战项目:通过金融App分析案例,应用了所学知识

这些技术对于理解和分析App的安全机制非常重要,同时也为开发更安全的应用提供了参考。需要注意的是,这些技术应当用于合法的安全研究和渗透测试,而不是恶意用途。

在实际应用中,我们需要根据目标App的具体情况选择合适的分析方法和绕过策略,同时要注意遵守相关法律法规。