#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逆向技术:
- APK解析:掌握了APK文件结构,学会了使用各种反编译工具
- Frida技术:深入了解了动态Hook技术,掌握了绕过各种安全检测的方法
- 安全绕过:学习了SSL Pinning、Root检测、模拟器检测等绕过技术
- 实战项目:通过金融App分析案例,应用了所学知识
这些技术对于理解和分析App的安全机制非常重要,同时也为开发更安全的应用提供了参考。需要注意的是,这些技术应当用于合法的安全研究和渗透测试,而不是恶意用途。
在实际应用中,我们需要根据目标App的具体情况选择合适的分析方法和绕过策略,同时要注意遵守相关法律法规。

