#SSL Pinning绕过技术
SSL Pinning(SSL证书锁定)是App安全防护的重要手段,但也是爬虫需要突破的关键点。
#SSL Pinning原理与绕过方法
#1. 传统SSL Pinning绕过
// Java层SSL Pinning绕过示例(Frida脚本)
Java.perform(function() {
// 绕过OkHttp3的CertificatePinner
try {
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(str, obj) {
console.log('[+] Bypassing OkHTTP v3.x Pinning');
return;
};
} catch(err) {
console.log('[-] OkHTTP v3.x Pinning not found');
}
// 绕过TrustManager
try {
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
console.log('[+] Bypassing TrustManagerImpl pinning: ' + host);
return untrustedChain;
};
} catch(err) {
console.log('[-] TrustManagerImpl not found');
}
// 绕过X509TrustManager
try {
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
var SSLContext = Java.use('javax.net.ssl.SSLContext');
// Hook SSLContext.init方法
SSLContext.init.overload('[Ljavax.net.ssl.KeyManager;', '[Ljavax.net.ssl.TrustManager;', 'java.security.SecureRandom').implementation = function(keyManager, trustManager, secureRandom) {
console.log('[+] Hooking SSLContext.init()');
SSLContext.init.call(this, keyManager, [X509TrustManager.$new()], secureRandom);
};
// 实现X509TrustManager接口
var TrustAllCerts = Java.registerClass({
name: 'com.example.TrustAllCerts',
implements: [X509TrustManager],
methods: {
checkClientTrusted: function(chain, authType) {},
checkServerTrusted: function(chain, authType) {},
getAcceptedIssuers: function() { return []; }
}
});
} catch(err) {
console.log('[-] X509TrustManager bypass not found');
}
});#2. SSL Pinning绕过Python脚本
import frida
import sys
import time
from typing import Optional, Dict, Any
class SSLBypassInjector:
"""SSL Pinning绕过注入器"""
def __init__(self, app_package: str):
self.app_package = app_package
self.device = None
self.session = None
self.script = None
# SSL Pinning绕过脚本
self.ssl_bypass_script = """
// SSL Pinning绕过脚本
Java.perform(function() {
// 绕过OkHttp3
try {
var CertificatePinner = Java.use('okhttp3.CertificatePinner');
CertificatePinner.check.overload('java.lang.String', 'java.util.List').implementation = function(str, obj) {
console.log('[+] Bypassing OkHTTP v3.x Pinning for: ' + str);
return;
};
console.log('[+] Hooked OkHTTP v3.x CertificatePinner');
} catch(e) {
console.log('[-] OkHTTP v3.x CertificatePinner not found: ' + e.message);
}
// 绕过OkHttp4
try {
var CertificatePinner4 = Java.use('okhttp3.internal.tls.CertificatePinnerChainCleaner');
console.log('[+] Hooked OkHTTP v4 CertificatePinnerChainCleaner');
} catch(e) {
console.log('[-] OkHTTP v4 CertificatePinnerChainCleaner not found: ' + e.message);
}
// 绕过TrustManager
try {
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
var SSLContext = Java.use('javax.net.ssl.SSLContext');
var TrustManager = Java.registerClass({
name: 'dev.asd.test.TrustManager',
implements: [X509TrustManager],
methods: {
checkClientTrusted: function(chain, authType) {},
checkServerTrusted: function(chain, authType) {},
getAcceptedIssuers: function() { return []; }
}
});
var TrustManagers = [TrustManager.$new()];
var SSLContext_init = SSLContext.init.overload(
'[Ljavax.net.ssl.KeyManager;',
'[Ljavax.net.ssl.TrustManager;',
'java.security.SecureRandom'
);
SSLContext_init.implementation = function(keyManager, trustManager, secureRandom) {
console.log('[+] Bypassing TrustManager init');
SSLContext_init.call(this, keyManager, TrustManagers, secureRandom);
};
console.log('[+] Hooked SSLContext.init');
} catch(e) {
console.log('[-] TrustManager bypass failed: ' + e.message);
}
// 绕过Apache HTTP Client
try {
var CustomSSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory');
CustomSSLSocketFactory.isSecure.implementation = function(socket) {
console.log('[+] Bypassing Apache HTTP Client SSL');
return true;
};
console.log('[+] Hooked Apache HTTP Client SSLSocketFactory');
} catch(e) {
console.log('[-] Apache HTTP Client bypass not found: ' + e.message);
}
// 绕过NetworkSecurityConfig (Android 7.0+)
try {
var NetworkSecurityConfig = Java.use('android.security.net.config.NetworkSecurityConfigProvider');
console.log('[+] Hooked NetworkSecurityConfigProvider');
} catch(e) {
console.log('[-] NetworkSecurityConfigProvider not found: ' + e.message);
}
});
"""
def connect_device(self) -> bool:
"""连接到Frida设备"""
try:
# 获取USB设备
devices = frida.enumerate_usb_devices()
if not devices:
print("❌ 未找到USB连接的设备")
return False
# 选择第一个设备
self.device = devices[0]
print(f"✅ 连接到设备: {self.device.name} - {self.device.id}")
return True
except Exception as e:
print(f"❌ 连接设备失败: {e}")
return False
def attach_to_app(self) -> bool:
"""附加到目标应用"""
try:
# 附加到正在运行的应用或启动应用
self.session = self.device.attach(self.app_package)
print(f"✅ 附加到应用: {self.app_package}")
return True
except frida.ProcessNotFoundError:
print(f"⚠️ 应用未运行,尝试启动: {self.app_package}")
try:
pid = self.device.spawn([self.app_package])
self.session = self.device.attach(pid)
self.device.resume(pid) # 启动应用
print(f"✅ 启动并附加到应用: {self.app_package}")
return True
except Exception as e:
print(f"❌ 启动应用失败: {e}")
return False
except Exception as e:
print(f"❌ 附加到应用失败: {e}")
return False
def inject_ssl_bypass(self) -> bool:
"""注入SSL Pinning绕过脚本"""
try:
# 创建脚本
self.script = self.session.create_script(self.ssl_bypass_script)
# 加载脚本
self.script.load()
print("✅ SSL Pinning绕过脚本注入成功")
# 保持连接
print("ℹ️ SSL Pinning绕过已激活,开始监控网络请求...")
# 保持脚本运行
return True
except Exception as e:
print(f"❌ 脚本注入失败: {e}")
return False
def start_ssl_bypass(self) -> bool:
"""启动完整的SSL Pinning绕过流程"""
print(f"🚀 开始绕过 {self.app_package} 的SSL Pinning...")
if not self.connect_device():
return False
if not self.attach_to_app():
return False
if not self.inject_ssl_bypass():
return False
print("🎉 SSL Pinning绕过设置完成!")
print("📝 提示: 现在可以使用Fiddler/Charles等工具监控HTTPS流量")
return True
def cleanup(self):
"""清理资源"""
if self.script:
try:
self.script.unload()
print("🧹 SSL绕过脚本已卸载")
except:
pass
if self.session:
try:
self.session.detach()
print("🧹 会话已分离")
except:
pass
# Frida SSL Pinning绕过工具类
class AdvancedSSLBypass:
"""高级SSL Pinning绕过工具"""
@staticmethod
def create_universal_bypass_script() -> str:
"""创建通用的SSL Pinning绕过脚本"""
return """
console.log("[+] Universal SSL Pinning Bypass Script Loaded");
// 通用绕过函数
function universalBypass() {
// 1. 绕过OkHttp系列
var okHttpClasses = [
'okhttp3.CertificatePinner',
'okhttp3.internal.tls.CertificatePinnerChainCleaner',
'com.squareup.okhttp.CertificatePinner'
];
okHttpClasses.forEach(function(className) {
try {
var clazz = Java.use(className);
if (clazz.check) {
clazz.check.implementation = function() {
console.log('[+] Bypassing ' + className);
};
}
if (clazz.checkChain) {
clazz.checkChain.implementation = function() {
console.log('[+] Bypassing ' + className + ' chain check');
};
}
console.log('[+] Hooked ' + className);
} catch(e) {
console.log('[-] Not found: ' + className);
}
});
// 2. 绕过系统TrustManager
try {
var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
var SSLContext = Java.use('javax.net.ssl.SSLContext');
var MyTrustManager = Java.registerClass({
name: 'com.example.MyTrustManager',
implements: [X509TrustManager],
methods: {
checkClientTrusted: function(chain, authType) {
console.log('[+] checkClientTrusted bypassed');
},
checkServerTrusted: function(chain, authType) {
console.log('[+] checkServerTrusted bypassed for: ' + authType);
},
getAcceptedIssuers: function() {
console.log('[+] getAcceptedIssuers called');
return [];
}
}
});
// Hook SSLContext初始化
if (SSLContext.init.overload) {
var initOverloads = SSLContext.init.overloads;
initOverloads.forEach(function(overload) {
overload.implementation = function(keyManager, trustManager, secureRandom) {
console.log('[+] SSLContext.init bypassed');
this.init(keyManager, [MyTrustManager.$new()], secureRandom);
};
});
}
} catch(e) {
console.log('[-] System TrustManager bypass failed: ' + e.message);
}
// 3. 绕过WebView SSL
try {
var WebViewClient = Java.use('android.webkit.WebViewClient');
if (WebViewClient.onReceivedSslError) {
WebViewClient.onReceivedSslError.implementation = function(view, handler, error) {
console.log('[+] WebView SSL error bypassed');
handler.proceed(); // 忽略SSL错误
};
}
console.log('[+] Hooked WebViewClient SSL error handling');
} catch(e) {
console.log('[-] WebViewClient not found: ' + e.message);
}
// 4. 绕过Apache HttpClient
try {
var SSLSocketFactory = Java.use('org.apache.http.conn.ssl.SSLSocketFactory');
SSLSocketFactory.isSecure.implementation = function(socket) {
console.log('[+] Apache HttpClient SSL bypassed');
return true;
};
console.log('[+] Hooked Apache HttpClient');
} catch(e) {
console.log('[-] Apache HttpClient not found: ' + e.message);
}
// 5. 绕过 conscrypt
try {
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
if (TrustManagerImpl.verifyChain) {
TrustManagerImpl.verifyChain.implementation = function(untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData) {
console.log('[+] Conscrypt TrustManagerImpl bypassed for: ' + host);
return untrustedChain;
};
}
console.log('[+] Hooked Conscrypt TrustManagerImpl');
} catch(e) {
console.log('[-] Conscrypt classes not found: ' + e.message);
}
}
// 在Java加载完成后执行
Java.perform(universalBypass);
"""
@staticmethod
def bypass_with_spawn(app_package: str) -> Any:
"""使用spawn方式绕过SSL Pinning"""
try:
device = frida.get_usb_device(timeout=5)
pid = device.spawn([app_package])
session = device.attach(pid)
script_content = AdvancedSSLBypass.create_universal_bypass_script()
script = session.create_script(script_content)
script.load()
device.resume(pid)
print(f"[+] Spawn bypass activated for {app_package}")
return session, script
except Exception as e:
print(f"[-] Spawn bypass failed: {e}")
return None, None
def demonstrate_ssl_bypass():
"""演示SSL Pinning绕过"""
print("🔍 SSL Pinning绕过技术演示")
# 注意:实际使用时需要替换为真实的应用包名
app_package = "com.example.target.app" # 替换为实际目标应用包名
print(f"\n📦 目标应用: {app_package}")
print("⚠️ 注意: 需要设备已ROOT并安装Frida Server")
# 创建绕过工具实例
bypass_tool = SSLBypassInjector(app_package)
try:
# 启动绕过
success = bypass_tool.start_ssl_bypass()
if success:
print("\n🎯 SSL Pinning绕过已激活!")
print("📋 现在可以使用抓包工具监控HTTPS流量")
# 保持运行一段时间
try:
print("⏳ 保持连接中... 按Ctrl+C退出")
while True:
time.sleep(1)
except KeyboardInterrupt:
print("\n🛑 用户中断")
else:
print("\n❌ SSL Pinning绕过失败")
except Exception as e:
print(f"\n❌ 演示过程中出现错误: {e}")
finally:
# 清理资源
bypass_tool.cleanup()
if __name__ == "__main__":
demonstrate_ssl_bypass()
