当前位置:首页>安卓APP>Frida进阶——安卓应用 9 种 SSL Pinning 绕过方案

Frida进阶——安卓应用 9 种 SSL Pinning 绕过方案

  • 2026-04-15 19:35:26
Frida进阶——安卓应用 9 种 SSL Pinning 绕过方案

阅读提示抓包分析 Android 应用的方式远不止绕过 SSL Pinning 一种——HTTP Toolkit 一键注入、VPN 路由劫持、 R0capture、ecapture等手段层出不穷,很多场景下根本不需要与 SSL Pinning 正面硬刚。本文不做工具横向对比,仅聚焦于"SSL Pinning 这一对抗技术的原理与 Frida 绕过实现",适合想深入理解这一机制、或在通用方案失效时需要精准定位问题的读者继续阅读。

摘要:在 Android 逆向与安全测试中,SSL Pinning 是阻碍 HTTPS 流量抓包的最大障碍。本文从逆向工程师视角出发,系统梳理 9 种主流 SSL Pinning 实现方式,并给出对应的 Frida 绕过方案——从一键通杀脚本到 Native 层深度 Hook,覆盖从入门到高级对抗的绝大部分场景。


前言

当你用 Charles / Burp Suite / mitmproxy 等工具抓包一个 Android 应用时,如果遇到以下症状:

  • 应用显示"网络错误"或"连接失败"
  • 抓包工具看到 CONNECT 请求但无法解密内容
  • Logcat 中出现 javax.net.ssl.SSLHandshakeException 或 Certificate pinning failure
  • 应用直接闪退或拒绝启动

大概率就是遇到了 SSL Pinning(证书锁定)。

SSL Pinning 的本质是:应用在代码中硬编码了服务器证书或公钥的指纹信息,即使你的中间人代理证书已被系统信任,也会因为指纹不匹配而拒绝建立连接。这意味着仅仅安装 CA 证书是不够的——我们需要 Frida 这样的动态插桩工具,在运行时修改校验逻辑,才能顺利抓包。

本文将详细介绍如何使用 Frida 逐一攻破各种 SSL Pinning 实现。


一、SSL Pinning 原理回顾

1.1 标准 TLS 握手 vs SSL Pinning

在标准 TLS 握手中,客户端只验证服务器证书是否由系统信任的 CA(Certificate Authority,证书颁发机构)签发。这意味着只要我们将 Burp/Charles 的 CA 证书安装到设备的系统信任存储中,就能作为中间人(Man-in-the-Middle, MITM)解密全部 HTTPS 流量。

SSL Pinning 在此基础上增加了额外校验:客户端在代码中硬编码了服务器证书或公钥的指纹(Pin),即使中间人的证书被系统信任,也会因指纹不匹配而拒绝连接。

下图展示了两种场景的对比——左侧为标准 TLS 握手(可抓包),右侧为启用 SSL Pinning 后(抓包失败):

SSL Pinning 原理:标准 TLS 握手 vs 证书锁定

核心区别:标准 TLS 只关心"证书链是否可信",而 SSL Pinning 还要求"证书/公钥是否是我预期的那个"。这个"预期"就是硬编码在应用中的 Pin 值。

1.2 Pin 的对象

SSL Pinning 可以锁定证书链中不同层级的信息:

Pin 对象
说明
证书更换影响
安全性
整个证书
 (Certificate Pinning)
比对完整的 DER 编码证书
证书更换后立即失效,必须更新 APK
最严格,但维护成本最高
公钥
 (Public Key Pinning)
比对证书中的 SubjectPublicKeyInfo (SPKI)
只要密钥不变,续签证书不影响
推荐,平衡安全与维护
公钥哈希
 (Hash Pinning)
比对公钥的 SHA-256 哈希值
同上,且传输/存储更紧凑
最常用的现代实现方式

绝大多数现代实现采用公钥哈希 Pinning,因为它在安全性和可维护性之间取得了最佳平衡——服务器续签证书时只要保持同一密钥对,客户端无需更新。


二、抓包环境准备

2.1 基础环境

# 安装 Frida 工具链
pip install frida-tools

# 确认设备连接(USB 调试已开启)
adb devices
frida-ls-devices

# 下载与设备架构匹配的 frida-server
# 从 https://github.com/frida/frida/releases 下载
# arm64 设备选择 frida-server-<version>-android-arm64
adb push frida-server-<version>-android-arm64 /data/local/tmp/frida-server
adb shell "chmod 755 /data/local/tmp/frida-server"

# 以 root 权限启动 frida-server(需要 Root 设备)
adb shell "su -c '/data/local/tmp/frida-server -D &'"

# 验证 frida-server 运行状态
frida-ps -U

版本匹配:frida-tools 和 frida-server 的主版本号必须一致(如都是 16.x.x),否则会出现连接错误。使用 frida --version 查看本地版本。

2.2 代理设置

# 方案 A:设置 Wi-Fi 代理(常规方式)
# 大部分应用会走系统代理
adb shell settings put global http_proxy <PC_IP>:8080

# 方案 B:iptables 透明代理(适用于不走系统代理的应用)
# 将所有 443 端口流量重定向到 Burp
adb shell "su -c 'iptables -t nat -A OUTPUT -p tcp --dport 443 -j DNAT --to-destination <PC_IP>:8080'"

# 清除代理设置
adb shell settings put global http_proxy :0

# 清除 iptables 规则
adb shell "su -c 'iptables -t nat -F OUTPUT'"

为什么有些应用不走系统代理:部分应用使用 OkHttp 或 Native 网络库时,可以显式设置 NO_PROXY,或者直接通过 IP + 端口连接服务器而不查询系统代理设置。此时必须使用 iptables 透明代理或 VPN 方式(如 ProxyDroid)强制转发流量。

2.3 安装 CA 证书

Android 7.0(API 24)开始,应用默认不信任用户安装的 CA 证书,因此需要将抓包工具的 CA 证书安装到系统信任存储

# 步骤 1:导出 Burp/Charles 的 CA 证书(DER 格式)
# Burp: Proxy -> Options -> Import/Export CA Certificate -> Export DER
# Charles: Help -> SSL Proxying -> Save Charles Root Certificate

# 步骤 2:转换为 PEM 格式,并用旧算法计算哈希(Android 使用 OpenSSL 旧哈希)
openssl x509 -inform DER -in cacert.der -out cacert.pem
HASH=$(openssl x509 -inform PEM -subject_hash_old -in cacert.pem | head -1)
cp cacert.pem "${HASH}.0"

# 步骤 3:推送到系统证书目录(需要 root + 可写系统分区)
adb root
adb remount
adb push "${HASH}.0" /system/etc/security/cacerts/
adb shell "chmod 644 /system/etc/security/cacerts/${HASH}.0"
adb reboot

Android 14+ 注意:从 Android 14 开始,/system 分区采用只读挂载(read-only),即使 adb remount 也无法写入。推荐使用 Magisk 模块 MagiskTrustUserCerts 或者 MoveCertificate 自动将用户证书提升为系统证书,无需修改系统分区。


三、通用 SSL Pinning 绕过方案

在逐一分析各种 SSL Pinning 实现之前,先介绍几种万能方案,它们能覆盖大部分常见场景,建议优先尝试

下图展示了 Frida 绕过 SSL Pinning 的整体架构——从 PC 端注入脚本,到替换应用进程内的 SSL 校验逻辑:

Frida 绕过 SSL Pinning 架构图

3.1 方案一:使用现成的通杀脚本

社区维护了多个成熟的 SSL Pinning 绕过脚本,覆盖了绝大多数 Java 层实现。对于大部分应用,直接使用这些脚本即可成功抓包,无需编写任何代码。

objection

objection 是基于 Frida 的自动化安全测试工具,内置了 SSL Pinning 绕过功能:

# 安装 objection
pip install objection

# 以 spawn 方式启动目标应用并进入交互模式
objection -g <package_name> explore

# 在 objection 交互终端中执行一键绕过
android sslpinning disable

原理:objection 内部会 Hook SSLContext.init()TrustManagerImpl.verifyChain()OkHttp CertificatePinner.check() 等关键方法,将校验逻辑替换为空实现。

frida-multiple-unpinning

这是目前覆盖面最广的通杀脚本之一,由社区持续维护:

# 直接使用(-f 表示 spawn 模式启动应用)
frida -U -f <package_name> -l frida-multiple-unpinning.js

它内部覆盖了以下实现的绕过:

  • TrustManagerImpl (Android 默认)
  • OkHttp CertificatePinner (v3/v4)
  • TrustKit
  • Conscrypt
  • HttpsURLConnection
  • 以及更多第三方实现

3.2 方案二:全局 TrustManager 替换

核心思路:替换所有 SSLContext.init() 调用中的 TrustManager 参数为一个接受任意证书的空实现。这是所有 Java 层绕过的基础原理。

Java.perform(function () {
// 第一步:注册一个信任所有证书的 TrustManager 实现类
// Java.registerClass 会在 Dalvik/ART 虚拟机中动态创建一个新类
var TrustManager = Java.registerClass({
name"com.frida.TrustAllManager",
implements: [Java.use("javax.net.ssl.X509TrustManager")],
methods: {
// 不校验客户端证书(服务器侧调用,这里不关心)
checkClientTrustedfunction (chain, authType{ },
// 不校验服务器证书 —— 这是绕过 SSL Pinning 的关键
// 原始实现会在这里校验证书链/公钥哈希,抛出 CertificateException
// 我们直接 return,相当于"无条件信任"
checkServerTrustedfunction (chain, authType{ },
// 返回空数组,表示不限制可接受的 CA
getAcceptedIssuersfunction () {
return [];
            }
        }
    });

// 实例化并包装为数组(SSLContext.init 接受 TrustManager[])
var trustAllManager = TrustManager.$new();
var trustManagers = Java.array("javax.net.ssl.TrustManager", [trustAllManager]);

// 第二步:Hook SSLContext.init(),替换第二个参数(TrustManager 数组)
// 所有 Java 层 HTTPS 实现最终都会调用这个方法来设置 SSL 参数
var SSLContext = Java.use("javax.net.ssl.SSLContext");
    SSLContext.init.overload(
"[Ljavax.net.ssl.KeyManager;",
"[Ljavax.net.ssl.TrustManager;",
"java.security.SecureRandom"
    ).implementation = function (keyManager, tm, secureRandom{
console.log("[*] SSLContext.init() intercepted - replacing TrustManager");
// 保留原始 keyManager(mTLS 场景需要),只替换 TrustManager
this.init(keyManager, trustManagers, secureRandom);
    };

// 第三步:同时绕过 HostnameVerifier(主机名校验)
// 某些实现会在 TrustManager 之外额外校验主机名
var HostnameVerifier = Java.registerClass({
name"com.frida.TrustAllHostnameVerifier",
implements: [Java.use("javax.net.ssl.HostnameVerifier")],
methods: {
verifyfunction (hostname, session{
returntrue// 任意主机名都通过
            }
        }
    });

// 替换全局默认的 HostnameVerifier
var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
    HttpsURLConnection.setDefaultHostnameVerifier(HostnameVerifier.$new());

// Hook 实例级别的 setHostnameVerifier,防止应用在连接时重新设置
    HttpsURLConnection.setHostnameVerifier.implementation = function (verifier{
console.log("[*] HostnameVerifier replaced");
this.setHostnameVerifier(HostnameVerifier.$new());
    };

console.log("[+] Global TrustManager & HostnameVerifier bypass installed");
});

这个方案的本质:SSL Pinning 无论怎么实现,最终都要经过 SSLContext.init() 设置 TrustManager。替换这个入口就能拦截绝大多数 Java 层 pinning。但它对 Native 层(如 OpenSSL/BoringSSL)的 pinning 无效——那些实现根本不经过 Java API。


四、针对各种 SSL Pinning 实现的绕过方案

Android 应用实现 SSL Pinning 的方式多种多样,下图是 9 种主流实现的全景概览,按 Java 层和 Native 层分类,并标注了各自的 Frida Hook 点和绕过难度:

Android SSL Pinning 实现方式全景图

4.1 Network Security Configuration

实现特征

  • res/xml/network_security_config.xml 中配置 <pin-set> 节点
  • AndroidManifest.xml 中声明 android:networkSecurityConfig="@xml/network_security_config"
  • Android 7.0 (API 24) 及以上系统原生支持,Google 官方推荐的 pinning 方式

识别方式

# 反编译 APK 后搜索配置文件
# 使用 apktool 或 jadx 反编译
grep -r "pin-set" res/xml/
grep -r "networkSecurityConfig" AndroidManifest.xml

典型的配置文件示例:

<!-- res/xml/network_security_config.xml -->
<network-security-config>
<domain-config>
<domainincludeSubdomains="true">api.example.com</domain>
<pin-setexpiration="2025-12-31">
<!-- 主 pin:服务器当前公钥的 SHA-256 哈希 -->
<pindigest="SHA-256">A7b...mP2=</pin>
<!-- 备份 pin:备用密钥的哈希,防止主密钥泄露时无法更新 -->
<pindigest="SHA-256">X3f...9kQ=</pin>
</pin-set>
</domain-config>
</network-security-config>

绕过方案:Hook NetworkSecurityTrustManager 的证书校验方法,使其直接返回而不执行 Pin 校验。

Java.perform(function () {
try {
// Android 7.0+ 的 NetworkSecurityConfig 框架使用 NetworkSecurityTrustManager
// 作为实际执行 pin 校验的 TrustManager 实现
var NetworkSecurityTrustManager = Java.use(
"android.security.net.config.NetworkSecurityTrustManager"
        );

// 两参数版本的 checkServerTrusted(基础校验)
        NetworkSecurityTrustManager.checkServerTrusted.overload(
"[Ljava.security.cert.X509Certificate;",
"java.lang.String"
        ).implementation = function (certs, authType{
console.log("[*] NetworkSecurityTrustManager.checkServerTrusted() bypassed");
// 直接 return,不执行任何校验
return;
        };

// 三参数版本的 checkServerTrusted(扩展校验,带 hostname)
// 返回值是受信任的证书列表
        NetworkSecurityTrustManager.checkServerTrusted.overload(
"[Ljava.security.cert.X509Certificate;",
"java.lang.String",
"java.lang.String"
        ).implementation = function (certs, authType, host{
console.log("[*] NetworkSecurityTrustManager.checkServerTrusted(host) bypassed for: " + host);
// 返回空列表表示校验通过
return Java.use("java.util.ArrayList").$new();
        };
    } catch (e) {
console.log("[-] NetworkSecurityTrustManager not found: " + e);
    }

try {
// RootTrustManager 是 NetworkSecurityConfig 框架的顶层分发器
// 它根据域名匹配将请求路由到不同的 NetworkSecurityTrustManager
var RootTrustManager = Java.use(
"android.security.net.config.RootTrustManager"
        );
        RootTrustManager.checkServerTrusted.overload(
"[Ljava.security.cert.X509Certificate;",
"java.lang.String"
        ).implementation = function (certs, authType{
console.log("[*] RootTrustManager.checkServerTrusted() bypassed");
return;
        };
    } catch (e) {
console.log("[-] RootTrustManager not found: " + e);
    }
});

替代方案(非 Frida):直接修改反编译后的 network_security_config.xml,移除 <pin-set> 节点后重打包签名。这种方式更简单,但会触发应用的签名校验(如果有的话)。


4.2 OkHttp CertificatePinner

实现特征

  • 使用 okhttp3.CertificatePinner 或旧版 com.squareup.okhttp.CertificatePinner
  • 调用 .certificatePinner(pinner) 构建 OkHttpClient
  • 使用极其广泛:Retrofit(目前 Android 最主流的网络框架)底层就是 OkHttp

识别方式

# 在反编译代码中搜索特征字符串
grep -r "CertificatePinner" --include="*.java" --include="*.smali"
# 搜索 pin 哈希格式(sha256/ 前缀是 OkHttp 特有的)
grep -r "sha256/" --include="*.java" --include="*.smali"

绕过方案:Hook CertificatePinner.check() 方法使其直接返回。该方法校验失败时会抛出 SSLPeerUnverifiedException,我们让它直接 return 就跳过了校验。

Java.perform(function () {
// ========== OkHttp 3.x ==========
try {
var CertificatePinner = Java.use("okhttp3.CertificatePinner");

// check(String hostname, List<Certificate> peerCertificates)
// 这是 OkHttp 3.x 的标准签名
        CertificatePinner.check.overload(
"java.lang.String",
"java.util.List"
        ).implementation = function (hostname, peerCertificates{
console.log("[*] OkHttp3 CertificatePinner.check() bypassed for: " + hostname);
// 原方法校验失败会抛 SSLPeerUnverifiedException
// 直接 return 表示"校验通过"(void 方法)
return;
        };

// ========== OkHttp 4.x (Kotlin) ==========
// OkHttp 4.x 使用 Kotlin 重写,方法签名变为 check$okhttp
// 参数类型也不同:第二个参数是 Function0(惰性求值的证书列表)
try {
            CertificatePinner.check$okhttp.overload(
"java.lang.String",
"kotlin.jvm.functions.Function0"
            ).implementation = function (hostname, fn{
console.log("[*] OkHttp4 CertificatePinner.check$okhttp() bypassed for: " + hostname);
return;
            };
        } catch (e) {
// OkHttp 3.x 没有这个方法,忽略
        }
    } catch (e) {
console.log("[-] okhttp3.CertificatePinner not found: " + e);
    }

// OkHttp 2.x 包名为 com.squareup.okhttp(极老版本,现代应用已基本不再使用)
// 若确需兼容,将上方 "okhttp3" 替换为 "com.squareup.okhttp" 即可
});

处理混淆场景:很多应用使用 ProGuard/R8 混淆了 OkHttp 的类名(如 okhttp3.CertificatePinner 变成了 a.b.c)。此时直接 Hook 类名会失败,需要通过特征码定位混淆后的类名:

Java.perform(function () {
// 枚举所有已加载的类,通过结构特征匹配 CertificatePinner
// CertificatePinner 的特征:
//   1. 有 check 方法(参数为 String + List)
//   2. 内部持有 Map 或 Set 类型字段(存储 hostname -> pins 映射)
//   3. 代码中包含 "sha256/" 字符串常量
    Java.enumerateLoadedClasses({
onMatchfunction (className{
// 混淆后类名通常很短(如 a.b, x.y.z)
if (className.match(/^[a-z]\.[a-z]$/)) {
try {
var cls = Java.use(className);
var methods = cls.class.getDeclaredMethods();
for (var i = 0; i < methods.length; i++) {
if (methods[i].toString().indexOf("check") !== -1) {
var fields = cls.class.getDeclaredFields();
for (var j = 0; j < fields.length; j++) {
if (fields[j].getType().getName() === "java.util.Map" ||
                                    fields[j].getType().getName() === "java.util.Set") {
console.log("[?] Possible CertificatePinner: " + className);
                                }
                            }
                        }
                    }
                } catch (e) { }
            }
        },
onCompletefunction () { }
    });
});

更高效的定位方式:直接搜索 smali 代码中的 "sha256/" 字符串常量,然后反向追踪使用该常量的类——这个类极大概率就是混淆后的 CertificatePinner


4.3 自定义 TrustManager

实现特征

  • 应用自己实现了 javax.net.ssl.X509TrustManager 接口
  • 在 checkServerTrusted() 方法中编写自定义校验逻辑(比对证书指纹、公钥哈希等)
  • 校验失败时抛出 CertificateException

识别方式

# 搜索自定义 TrustManager 实现
grep -r "checkServerTrusted" --include="*.java"
grep -r "X509TrustManager" --include="*.java"

绕过方案:同时 Hook 系统默认的 TrustManagerImpl 和所有自定义 X509TrustManager 实现类。

Java.perform(function () {
var X509TrustManager = Java.use("javax.net.ssl.X509TrustManager");
var TrustManagerImpl = Java.use("com.android.org.conscrypt.TrustManagerImpl");

// 1. Hook 系统默认的 TrustManagerImpl(Android 内置的 Conscrypt 实现)
// verifyChain 是实际执行证书链验证的核心方法
    TrustManagerImpl.verifyChain.implementation = function (
        untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData
{
console.log("[*] TrustManagerImpl.verifyChain() bypassed for: " + host);
// 直接返回传入的证书链,表示"全部信任"
return untrustedChain;
    };

// 2. 枚举并 Hook 所有自定义 X509TrustManager 实现类
// 这样即使应用使用了自定义实现也能被覆盖
    Java.enumerateLoadedClasses({
onMatchfunction (className{
try {
var cls = Java.use(className);
// 检查是否实现了 X509TrustManager 接口
if (cls.class && X509TrustManager.class.isAssignableFrom(cls.class)) {
// 排除系统类和我们自己注入的类
if (className.indexOf("com.android.") === -1 &&
                        className.indexOf("com.frida.") === -1) {
console.log("[*] Found custom TrustManager: " + className);

// Hook 两参数版本
try {
                            cls.checkServerTrusted.overload(
"[Ljava.security.cert.X509Certificate;",
"java.lang.String"
                            ).implementation = function () {
console.log("[*] " + className + ".checkServerTrusted() bypassed");
return;
                            };
                        } catch (e) { }

// Hook 三参数版本(Android 扩展接口 X509ExtendedTrustManager)
try {
                            cls.checkServerTrusted.overload(
"[Ljava.security.cert.X509Certificate;",
"java.lang.String",
"java.lang.String"
                            ).implementation = function () {
console.log("[*] " + className + ".checkServerTrusted(host) bypassed");
return Java.use("java.util.ArrayList").$new();
                            };
                        } catch (e) { }
                    }
                }
            } catch (e) { }
        },
onCompletefunction () {
console.log("[+] Custom TrustManager scan complete");
        }
    });
});

4.4 自定义 HostnameVerifier

实现特征

  • 实现 javax.net.ssl.HostnameVerifier 接口
  • 在 verify(hostname, session) 方法中比对证书中的主机名或公钥指纹

绕过方案:枚举所有 HostnameVerifier 实现类,强制 verify() 返回 true

Java.perform(function () {
var HostnameVerifier = Java.use("javax.net.ssl.HostnameVerifier");

// 枚举所有 HostnameVerifier 实现类并 Hook
    Java.enumerateLoadedClasses({
onMatchfunction (className{
try {
var cls = Java.use(className);
if (cls.class && HostnameVerifier.class.isAssignableFrom(cls.class)) {
// 排除我们自己注入的类
if (className.indexOf("com.frida.") === -1) {
                        cls.verify.overload(
"java.lang.String",
"javax.net.ssl.SSLSession"
                        ).implementation = function (hostname, session{
console.log("[*] " + className + ".verify() bypassed for: " + hostname);
returntrue// 主机名校验始终通过
                        };
                    }
                }
            } catch (e) { }
        },
onCompletefunction () { }
    });

// 单独处理 OkHttp 的内置 HostnameVerifier
// OkHttp 使用自己的 OkHostnameVerifier,不一定被上面的枚举覆盖
try {
var OkHostnameVerifier = Java.use("okhttp3.internal.tls.OkHostnameVerifier");
        OkHostnameVerifier.verify.overload(
"java.lang.String",
"javax.net.ssl.SSLSession"
        ).implementation = function (hostname, session{
console.log("[*] OkHostnameVerifier.verify() bypassed for: " + hostname);
returntrue;
        };
// OkHostnameVerifier 还有一个直接接受证书的重载
        OkHostnameVerifier.verify.overload(
"java.lang.String",
"java.security.cert.X509Certificate"
        ).implementation = function (hostname, cert{
console.log("[*] OkHostnameVerifier.verify(cert) bypassed for: " + hostname);
returntrue;
        };
    } catch (e) { }
});

4.5 嵌入证书文件 (KeyStore 方式)

实现特征

  • assets/ 或 res/raw/ 目录下存在证书文件:.cer.pem.bks(BouncyCastle KeyStore)、.p12(PKCS12)
  • 代码中通过 KeyStore.load() 加载这些证书
  • 通过 TrustManagerFactory.init(keyStore) 构建只信任特定证书的 TrustManager

识别方式

# 反编译 APK 后搜索证书文件
find . -name "*.cer" -o -name "*.pem" -o -name "*.bks" -o -name "*.p12" -o -name "*.crt"
# 搜索代码中的 KeyStore 使用
grep -r "TrustManagerFactory" --include="*.java"
grep -r "KeyStore" --include="*.java"

绕过方案:Hook TrustManagerFactory.init(),忽略应用自定义的 KeyStore,改用系统默认信任存储。

Java.perform(function () {
// Hook TrustManagerFactory.init(),将参数替换为 null
// 传入 null 表示使用系统默认信任存储(包含我们安装的 Burp CA 证书)
var TrustManagerFactory = Java.use("javax.net.ssl.TrustManagerFactory");
    TrustManagerFactory.init.overload("java.security.KeyStore").implementation = function (keyStore{
console.log("[*] TrustManagerFactory.init() intercepted - using default system trust store");
this.init(null); // null = 系统默认 KeyStore
    };

// 可选:监控 KeyStore 加载过程,获取额外信息
var KeyStore = Java.use("java.security.KeyStore");
    KeyStore.getCertificate.implementation = function (alias{
console.log("[*] KeyStore.getCertificate() called for alias: " + alias);
returnthis.getCertificate(alias);
    };

// 建议同时配合全局 TrustManager 替换(见 3.2)效果更好
console.log("[+] KeyStore-based pinning bypass installed");
});

4.6 第三方库 (TrustKit / Conscrypt)

TrustKit

TrustKit 是一个跨平台的 SSL Pinning 库,在 Android 上它封装了标准的 TrustManager,额外提供了 pinning 失败上报功能(会向服务器报告绕过尝试)。

识别方式

grep -r "TrustKit" --include="*.java" --include="*.smali"
grep -r "com.datatheorem.android.trustkit" --include="*.java"

绕过方案

Java.perform(function () {
try {
// Hook TrustKit 的核心校验类
var PinningTrustManager = Java.use(
"com.datatheorem.android.trustkit.pinning.PinningTrustManager"
        );
        PinningTrustManager.checkServerTrusted.implementation = function (chain, authType{
console.log("[*] TrustKit PinningTrustManager.checkServerTrusted() bypassed");
return// 跳过 pin 校验
        };
    } catch (e) {
console.log("[-] TrustKit not found: " + e);
    }

try {
// 同时禁用 TrustKit 的失败上报功能
// 防止服务器端收到"pinning 被绕过"的告警
var BackgroundReporter = Java.use(
"com.datatheorem.android.trustkit.reporting.BackgroundReporter"
        );
        BackgroundReporter.pinValidationFailed.implementation = function () {
console.log("[*] TrustKit pin failure report suppressed");
return// 不发送上报请求
        };
    } catch (e) { }
});

Conscrypt

Conscrypt 是 Google 维护的 Java 安全库(Java Security Provider),底层使用 BoringSSL。Android 系统内置了 Conscrypt 作为默认的 TLS 实现,部分应用也会额外引入独立版本。

识别方式

grep -r "org.conscrypt" --include="*.java" --include="*.smali"
grep -r "Conscrypt.newTrustManager" --include="*.java"

绕过方案

Java.perform(function () {
try {
// 独立引入的 Conscrypt(第三方库版本)
var ConscryptTrustManager = Java.use("org.conscrypt.TrustManagerImpl");
        ConscryptTrustManager.verifyChain.implementation = function (
            untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData
{
console.log("[*] Conscrypt TrustManagerImpl.verifyChain() bypassed for: " + host);
return untrustedChain; // 返回原证书链,表示信任
        };
    } catch (e) {
console.log("[-] Conscrypt (external) TrustManagerImpl not found: " + e);
    }

try {
// Android 系统内置的 Conscrypt(包名前缀为 com.android.org.conscrypt)
var PlatformConscrypt = Java.use("com.android.org.conscrypt.TrustManagerImpl");
        PlatformConscrypt.verifyChain.implementation = function (
            untrustedChain, trustAnchorChain, host, clientAuth, ocspData, tlsSctData
{
console.log("[*] Platform Conscrypt verifyChain() bypassed for: " + host);
return untrustedChain;
        };
    } catch (e) { }
});

4.7 WebView SSL Pinning

实现特征

  • 在 WebViewClient.onReceivedSslError() 回调中进行证书校验
  • 或在 WebViewClient.shouldInterceptRequest() 中拦截请求后使用 OkHttp + CertificatePinner 发起

绕过方案:Hook onReceivedSslError,强制调用 handler.proceed() 继续加载。

Java.perform(function () {
var WebViewClient = Java.use("android.webkit.WebViewClient");

// Hook 基类的 onReceivedSslError,强制 proceed
    WebViewClient.onReceivedSslError.implementation = function (view, handler, error{
console.log("[*] WebView SSL error bypassed: " + error.toString());
        handler.proceed(); // 忽略 SSL 错误,继续加载页面
    };

// 枚举所有自定义 WebViewClient 子类
// 因为子类可能覆盖了 onReceivedSslError 并在内部做额外校验
    Java.enumerateLoadedClasses({
onMatchfunction (className{
try {
var cls = Java.use(className);
if (cls.class.getSuperclass() &&
                    cls.class.getSuperclass().getName() === "android.webkit.WebViewClient") {
console.log("[*] Found WebViewClient subclass: " + className);
try {
                        cls.onReceivedSslError.implementation = function (view, handler, error{
console.log("[*] " + className + ".onReceivedSslError() bypassed");
                            handler.proceed();
                        };
                    } catch (e) { }
                }
            } catch (e) { }
        },
onCompletefunction () { }
    });
});

注意:如果 WebView 的 SSL Pinning 是通过 shouldInterceptRequest 拦截后用 OkHttp 发起请求来实现的,那么需要同时绕过 OkHttp 的 CertificatePinner(见 4.2 节)。


4.8 Native 层 SSL Pinning(重点难点)

实现特征

  • 应用使用 NDK 编写的网络库(如 libcurl、BoringSSL、OpenSSL)
  • 证书校验逻辑在 .so 文件中,Java 层完全看不到 pinning 相关代码
  • 使用 SSL_CTX_set_verify() 或 SSL_CTX_set_custom_verify() 注册 C/C++ 层的验证回调
  • 常见于金融类、支付类、游戏类、短视频类应用——这些应用追求更高的逆向门槛

识别方式

# 查看应用使用的 native 库
ls lib/arm64-v8a/
# 关注: libssl.so, libboringssl.so, libttboringssl.so, libcurl.so, libnative-lib.so 等

# 使用 radare2 分析 SO 文件中的 SSL 相关导入/导出
rabin2 -E libboringssl.so | grep -iE "SSL_CTX_set|SSL_get_verify|custom_verify"
rabin2 -i libnative-lib.so | grep -i ssl

4.8.1 系统 OpenSSL / BoringSSL(libssl.so)

Android 系统内置的 TLS 库(底层是 BoringSSL)以 libssl.so 对外暴露,大多数 NDK 应用直接链接系统库。

绕过方案 A:将验证模式强制改为 SSL_VERIFY_NONE

// Hook SSL_CTX_set_verify —— 设置 SSL 上下文的验证模式
// mode = 0 (SSL_VERIFY_NONE) 表示不校验对端证书
Interceptor.attach(Module.findExportByName("libssl.so""SSL_CTX_set_verify"), {
onEnterfunction (args{
console.log("[*] SSL_CTX_set_verify() called, mode=" + args[1].toInt32());
        args[1] = ptr(0x0); // SSL_VERIFY_NONE
        args[2] = ptr(0x0); // 清除自定义验证回调
    }
});

// 同样 Hook SSL_set_verify(per-connection 级别,覆盖更细粒度的设置)
Interceptor.attach(Module.findExportByName("libssl.so""SSL_set_verify"), {
onEnterfunction (args{
        args[1] = ptr(0x0);
        args[2] = ptr(0x0);
console.log("[*] SSL_set_verify() -> NONE");
    }
});

绕过方案 B:强制验证结果为成功。

// SSL_get_verify_result 返回 X509_V_OK (0) 表示验证通过
Interceptor.attach(Module.findExportByName("libssl.so""SSL_get_verify_result"), {
onLeavefunction (retval{
        retval.replace(ptr(0x0)); // 强制返回 X509_V_OK = 0
console.log("[*] SSL_get_verify_result -> X509_V_OK");
    }
});

// X509_verify_cert 返回 1 表示验证通过
Interceptor.attach(Module.findExportByName("libcrypto.so""X509_verify_cert"), {
onLeavefunction (retval{
        retval.replace(ptr(0x1));
console.log("[*] X509_verify_cert -> 1 (success)");
    }
});

绕过方案 C:Hook 自定义验证回调(需配合逆向分析定位回调地址)。

// 先通过 Hook SSL_CTX_set_verify 捕获回调地址,再替换其返回值
Interceptor.attach(Module.findExportByName("libssl.so""SSL_CTX_set_verify"), {
onEnterfunction (args{
if (!args[2].isNull()) {
var callbackAddr = args[2];
console.log("[*] Custom verify callback @ " + callbackAddr +
" (" + Process.findModuleByAddress(callbackAddr).name + ")");
            Interceptor.attach(callbackAddr, {
onLeavefunction (retval{
                    retval.replace(ptr(0x1)); // 强制返回 1(通过)
console.log("[*] Custom verify callback -> 1");
                }
            });
        }
    }
});

静态编译场景:如果应用将 BoringSSL 静态编译进了自己的 SO 文件(如 libnative-lib.so),函数不会出现在 libssl.so 导出表中。需要用 Ghidra/IDA 通过字符串引用("ssl_verify_peer_cert" 等错误日志)或函数签名特征定位后,直接 Hook 绝对地址。


4.8.2 独立打包的 BoringSSL(libboringssl.so)

部分应用(尤其是 Google 系、Chromium 内核的 WebView、以及一些高安全性应用)会在 APK 中打包独立的 libboringssl.so,而不是链接系统 libssl.so。相比 OpenSSL,BoringSSL 引入了更灵活的 SSL_CTX_set_custom_verify API,其回调的返回值类型与 OpenSSL 不同:

返回值
含义
ssl_verify_ok
 (0)
验证通过
ssl_verify_invalid
 (1)
验证失败
ssl_verify_retry
 (2)
异步等待,稍后重试
var libName = "libboringssl.so";

// --- 1. Hook SSL_CTX_set_custom_verify(BoringSSL 专有 API)---
// 原型: void SSL_CTX_set_custom_verify(SSL_CTX *ctx, int mode,
//        enum ssl_verify_result_t (*callback)(SSL *ssl, uint8_t *out_alert))
var customVerifyPtr = Module.findExportByName(libName, "SSL_CTX_set_custom_verify");
if (customVerifyPtr) {
    Interceptor.attach(customVerifyPtr, {
onEnterfunction (args{
console.log("[*] SSL_CTX_set_custom_verify mode=" + args[1].toInt32());
if (!args[2].isNull()) {
// 注入一个始终返回 ssl_verify_ok (0) 的假回调
// 'int' 对应枚举类型 ssl_verify_result_t
var fakeVerify = new NativeCallback(function (ssl, out_alert{
console.log("[*] BoringSSL custom verify callback -> ssl_verify_ok");
return0// ssl_verify_ok
                }, "int", ["pointer""pointer"]);
                args[2] = fakeVerify;
            }
        }
    });
else {
console.log("[-] SSL_CTX_set_custom_verify not exported in " + libName);
}

// --- 2. Hook SSL_CTX_set_cert_verify_callback(另一套回调机制)---
// 原型: void SSL_CTX_set_cert_verify_callback(SSL_CTX *ctx,
//        int (*cb)(X509_STORE_CTX *, void *), void *arg)
// 返回 1 = 成功, 0 = 失败
var certVerifyCbPtr = Module.findExportByName(libName, "SSL_CTX_set_cert_verify_callback");
if (certVerifyCbPtr) {
    Interceptor.attach(certVerifyCbPtr, {
onEnterfunction (args{
if (!args[1].isNull()) {
var fakeCallback = new NativeCallback(function (storeCtx, arg{
console.log("[*] cert_verify_callback -> 1 (always pass)");
return1;
                }, "int", ["pointer""pointer"]);
                args[1] = fakeCallback;
            }
        }
    });
}

// --- 3. 兜底:强制验证模式和验证结果 ---
["SSL_CTX_set_verify""SSL_set_verify"].forEach(function (fnName{
var addr = Module.findExportByName(libName, fnName);
if (addr) {
        Interceptor.attach(addr, {
onEnterfunction (args{
                args[1] = ptr(0x0); // SSL_VERIFY_NONE
                args[2] = ptr(0x0);
console.log("[*] " + libName + " " + fnName + " -> NONE");
            }
        });
    }
});

var getVerifyResultPtr = Module.findExportByName(libName, "SSL_get_verify_result");
if (getVerifyResultPtr) {
    Interceptor.attach(getVerifyResultPtr, {
onLeavefunction (retval{
            retval.replace(ptr(0x0)); // X509_V_OK = 0
        }
    });
}

4.8.3 字节跳动系应用:libttboringssl.so

libttboringssl.so 是字节跳动(ByteDance)基于 BoringSSL 深度定制的 TLS 库,抖音、TikTok、今日头条、西瓜视频、飞书等旗下主流应用均使用此库。相比标准 BoringSSL,它有以下特点:

  • 导出符号精简:只暴露必要的 SSL API,内部验证函数多数为非导出符号
  • 额外 Pinning 逻辑:在 TLS 握手握手多阶段注入自定义证书锁定检查
  • 符号混淆/重命名:部分内部函数经过了名称混淆

Step 1:枚举可用的导出符号

# 先确认库是否存在及其导出情况
rabin2 -E lib/arm64-v8a/libttboringssl.so | grep -iE "SSL|verify|cert" | head -30

Step 2:Hook 导出的标准验证函数

var ttLib = "libttboringssl.so";

// 等待库加载(部分应用延迟加载 so)
functionhookTTBoringSSL() {
var ttMod = Process.findModuleByName(ttLib);
if (!ttMod) {
console.log("[-] " + ttLib + " not loaded yet");
return;
    }
console.log("[+] " + ttLib + " base: " + ttMod.base + "  size: " + ttMod.size);

// 尝试 Hook 所有可能的验证入口
var targets = {
// BoringSSL 标准接口(通常仍然导出)
"SSL_CTX_set_custom_verify"function (args{
if (!args[2].isNull()) {
                args[2] = new NativeCallback(function (ssl, out_alert{
console.log("[*] ttBoringSSL custom_verify -> ssl_verify_ok");
return0;
                }, "int", ["pointer""pointer"]);
            }
        },
"SSL_CTX_set_cert_verify_callback"function (args{
if (!args[1].isNull()) {
                args[1] = new NativeCallback(function (storeCtx, arg{
console.log("[*] ttBoringSSL cert_verify_callback -> 1");
return1;
                }, "int", ["pointer""pointer"]);
            }
        },
"SSL_CTX_set_verify"function (args{
            args[1] = ptr(0x0);
            args[2] = ptr(0x0);
console.log("[*] ttBoringSSL SSL_CTX_set_verify -> NONE");
        },
"SSL_set_verify"function (args{
            args[1] = ptr(0x0);
            args[2] = ptr(0x0);
console.log("[*] ttBoringSSL SSL_set_verify -> NONE");
        }
    };

Object.keys(targets).forEach(function (fnName{
var addr = Module.findExportByName(ttLib, fnName);
if (addr) {
console.log("[+] Hooking " + fnName + " @ " + addr);
            Interceptor.attach(addr, { onEnter: targets[fnName] });
        } else {
console.log("[-] " + fnName + " not exported in " + ttLib);
        }
    });

// 强制 SSL_get_verify_result 返回 X509_V_OK
var getVerifyAddr = Module.findExportByName(ttLib, "SSL_get_verify_result");
if (getVerifyAddr) {
        Interceptor.attach(getVerifyAddr, {
onLeavefunction (retval{ retval.replace(ptr(0x0)); }
        });
    }
}

// 如果库已加载则立即执行,否则监听模块加载事件
if (Process.findModuleByName(ttLib)) {
    hookTTBoringSSL();
else {
// 监听 dlopen,等待 libttboringssl.so 被加载
var dlopenPtr = Module.findExportByName(null"android_dlopen_ext") ||
                    Module.findExportByName(null"dlopen");
if (dlopenPtr) {
        Interceptor.attach(dlopenPtr, {
onEnterfunction (args{
this.libPath = args[0].readCString();
            },
onLeavefunction (retval{
if (this.libPath && this.libPath.indexOf("ttboringssl") !== -1) {
console.log("[+] " + ttLib + " loaded, installing hooks...");
                    hookTTBoringSSL();
                }
            }
        });
    }
}

Step 3:符号被剥离时——通过特征字节定位验证函数

当 SSL_CTX_set_custom_verify 等函数未导出时,可以借助库内特征字符串定位相关函数,再配合 IDA/Ghidra 进行静态分析后 Hook 偏移地址:

var ttMod = Process.findModuleByName("libttboringssl.so");
if (ttMod) {
// 在库的内存区域扫描特征字符串,辅助定位验证相关代码
var signatures = [
// "ssl_verify" 系列错误日志字符串(BoringSSL 源码中固定出现)
        { name"CERTIFICATE_VERIFY_FAILED"hex"43455254494649434154455f564552494659_4641494c4544" },
        { name"ssl_verify_peer_cert",      hex"73736c5f766572696679_706565725f63657274" }
    ];

    signatures.forEach(function (sig{
// 找到字符串后,用 IDA/Ghidra 交叉引用到调用函数,再 Hook 该偏移
console.log("[*] Search for '" + sig.name + "' to locate verify function via xref");
    });

// 示例:若已通过逆向确认某版本的 ssl_verify_peer_cert 偏移为 0x3A5678
// var verifyOffset = 0x3A5678;
// Interceptor.attach(ttMod.base.add(verifyOffset), {
//     onLeave: function(retval) { retval.replace(ptr(0x0)); }
// });
}

实战建议:抖音/TikTok 每次版本更新可能调整偏移,建议优先依赖导出符号 Hook(Step 2)。若导出符号均失效,可配合 Frida CodeShare 搜索社区针对该版本的现成脚本,或用 Ghidra 分析新版 so 后更新偏移。


4.8.4 libcurl(C/C++ 通用网络框架)

// libcurl 通过 curl_easy_setopt 设置 SSL 验证选项
// CURLOPT_SSL_VERIFYPEER = 64  —— 是否校验服务器证书
// CURLOPT_SSL_VERIFYHOST = 81  —— 是否校验主机名(2 = 开启,0 = 关闭)
Interceptor.attach(Module.findExportByName("libcurl.so""curl_easy_setopt"), {
onEnterfunction (args{
var option = args[1].toInt32();
if (option === 64 || option === 81) {
console.log("[*] curl_easy_setopt option=" + option + " -> 0 (disabled)");
            args[2] = ptr(0x0);
        }
    }
});

4.9 双向认证 (mTLS) 绕过

实现特征

  • 应用在 assets/ 中嵌入了客户端证书(.p12.pfx.bks
  • 代码中同时配置了 KeyManager(客户端证书)和 TrustManager(服务器验证)
  • 服务器端会校验客户端证书,拒绝没有有效客户端证书的连接

核心难点:mTLS 不仅需要绕过客户端对服务器的验证(SSL Pinning),还需要提取客户端证书和私钥交给抓包工具(Burp Suite),否则服务器会拒绝 Burp 的连接。

下图展示了完整的 mTLS 绕过三步流程:

mTLS 双向认证绕过流程

Step 1:Hook 提取客户端证书密码和别名信息。

Java.perform(function () {
// Hook KeyStore.load() —— 拦截证书加载过程
// 应用在这里加载 .p12/.bks 文件,我们可以获取密码
var KeyStore = Java.use("java.security.KeyStore");
    KeyStore.load.overload("java.io.InputStream""[C").implementation = function (stream, password{
console.log("[*] KeyStore.load() called");
// 打印密码(char[] 转 String)
if (password) {
console.log("    Password: " + Java.use("java.lang.String").$new(password));
        }
// 先执行原始加载
this.load(stream, password);

// 枚举 KeyStore 中的所有条目
var aliases = this.aliases();
while (aliases.hasMoreElements()) {
var alias = aliases.nextElement().toString();
console.log("    Alias: " + alias);
// 检查是否包含私钥(客户端证书必须包含私钥)
if (this.isKeyEntry(alias)) {
console.log("    -> Contains private key!");
            }
        }
    };

// Hook KeyManagerFactory.init() 获取密码确认
var KeyManagerFactory = Java.use("javax.net.ssl.KeyManagerFactory");
    KeyManagerFactory.init.overload(
"java.security.KeyStore""[C"
    ).implementation = function (keyStore, password{
console.log("[*] KeyManagerFactory.init()");
if (password) {
console.log("    Password: " + Java.use("java.lang.String").$new(password));
        }
this.init(keyStore, password);
    };
});

Step 2:导出客户端证书为 PKCS12 格式文件。

Java.perform(function () {
var KeyStore = Java.use("java.security.KeyStore");
    KeyStore.load.overload("java.io.InputStream""[C").implementation = function (stream, password{
// 先执行原始加载
this.load(stream, password);

// 将完整的 KeyStore(包含私钥 + 证书)导出为 PKCS12 文件
var FileOutputStream = Java.use("java.io.FileOutputStream");
var fos = FileOutputStream.$new("/data/local/tmp/client_cert.p12");
this.store(fos, password); // 使用原密码保护导出的文件
        fos.close();

console.log("[+] Client certificate exported to /data/local/tmp/client_cert.p12");
if (password) {
console.log("[+] Password: " + Java.use("java.lang.String").$new(password));
        }
    };
});

Step 3:将导出的证书从设备拉取到 PC,导入 Burp Suite。

# 从设备拉取导出的客户端证书
adb pull /data/local/tmp/client_cert.p12

# 在 Burp Suite 中导入客户端证书:
# Settings -> Network -> TLS -> Client TLS Certificates -> Add
# Destination host: *.example.com(或具体域名)
# Certificate file: 选择导出的 client_cert.p12
# Password: 输入 Step 1 中获取的密码

重要:导入客户端证书后,还需要同时运行 SSL Pinning 绕过脚本(绕过客户端对服务器的校验),双管齐下才能实现 mTLS 场景下的完整抓包。


五、高级对抗场景

5.1 证书校验代码被混淆

当应用使用 ProGuard/R8 混淆后,类名变成了 a.b.c、方法名变成了 a(),直接按类名 Hook 会失败。

应对策略

Java.perform(function () {
// 策略 1:通过接口枚举 —— 接口名不会被混淆
// Java 接口是 API 的一部分,ProGuard 不会重命名标准库接口
var X509TM = Java.use("javax.net.ssl.X509TrustManager");
    Java.choose(X509TM.class.getName(), {
onMatchfunction (instance{
console.log("[*] X509TrustManager instance: " + instance.getClass().getName());
// 输出混淆后的实际类名,然后可以针对性 Hook
        },
onCompletefunction () { }
    });

// 策略 2:Hook 异常构造函数,通过堆栈追溯校验位置
// SSL Pinning 失败时必定会抛出 CertificateException
// 我们 Hook 这个异常的构造函数,打印调用栈
var CertificateException = Java.use("java.security.cert.CertificateException");
    CertificateException.$init.overload("java.lang.String").implementation = function (msg{
console.log("[!] CertificateException: " + msg);
// 打印完整的 Java 调用栈 —— 栈中会包含混淆后的类名和方法名
console.log(Java.use("android.util.Log").getStackTraceString(
            Java.use("java.lang.Exception").$new()
        ));
this.$init(msg);
    };

// 策略 3:Hook SSLPeerUnverifiedException(OkHttp 抛出的异常)
var SSLPeerUnverifiedException = Java.use("javax.net.ssl.SSLPeerUnverifiedException");
    SSLPeerUnverifiedException.$init.overload("java.lang.String").implementation = function (msg{
console.log("[!] SSLPeerUnverifiedException: " + msg);
console.log(Java.use("android.util.Log").getStackTraceString(
            Java.use("java.lang.Exception").$new()
        ));
this.$init(msg);
    };
});

实战技巧:拿到异常堆栈后,找到栈中应用自己的类(非系统类),那就是执行校验的位置。接着用 jadx 查看该类的反编译代码,定位 check 或 verify 方法,编写针对性的 Hook。

5.2 多重 Pinning 组合

某些高安全应用(银行、支付、证券)同时使用多种 pinning 机制,构成"多层防御"。例如:Java 层用 OkHttp CertificatePinner + Network Security Config,Native 层再用 BoringSSL 做一次校验。

完整通杀脚本模板(覆盖 Java 层 + Native 层):

// universal_ssl_bypass.js
// 一站式覆盖 Java 层 + Native 层的完整绕过方案

Java.perform(function () {
console.log("[*] === Universal SSL Pinning Bypass ===");

// ========== 1. 全局 TrustManager 替换 ==========
var TrustAllManager = Java.registerClass({
name"com.bypass.TrustAllManager",
implements: [Java.use("javax.net.ssl.X509TrustManager")],
methods: {
checkClientTrustedfunction (chain, authType{ },
checkServerTrustedfunction (chain, authType{ },
getAcceptedIssuersfunction () return []; }
        }
    });
var trustAllManagers = Java.array(
"javax.net.ssl.TrustManager", [TrustAllManager.$new()]
    );

var SSLContext = Java.use("javax.net.ssl.SSLContext");
    SSLContext.init.overload(
"[Ljavax.net.ssl.KeyManager;",
"[Ljavax.net.ssl.TrustManager;",
"java.security.SecureRandom"
    ).implementation = function (km, tm, sr{
console.log("[+] SSLContext.init() -> TrustAll");
this.init(km, trustAllManagers, sr);
    };

// ========== 2. HostnameVerifier ==========
var TrustAllVerifier = Java.registerClass({
name"com.bypass.TrustAllVerifier",
implements: [Java.use("javax.net.ssl.HostnameVerifier")],
methods: {
verifyfunction () returntrue; }
        }
    });

var HttpsURLConnection = Java.use("javax.net.ssl.HttpsURLConnection");
    HttpsURLConnection.setDefaultHostnameVerifier(TrustAllVerifier.$new());

// ========== 3. OkHttp ==========
try {
var CertPinner = Java.use("okhttp3.CertificatePinner");
        CertPinner.check.overload("java.lang.String""java.util.List").implementation =
function () console.log("[+] OkHttp3 CertificatePinner bypassed"); };
    } catch (e) { }

// ========== 4. TrustManagerImpl (Android / Conscrypt) ==========
    ["com.android.org.conscrypt.TrustManagerImpl",
"org.conscrypt.TrustManagerImpl"].forEach(function (clsName{
try {
var TrustManagerImpl = Java.use(clsName);
            TrustManagerImpl.verifyChain.implementation = function (
                untrusted, trustAnchor, host, clientAuth, ocspData, tlsSctData
{
console.log("[+] " + clsName + ".verifyChain() bypassed for: " + host);
return untrusted;
            };
        } catch (e) { }
    });

// ========== 5. NetworkSecurityConfig ==========
try {
var NSTM = Java.use("android.security.net.config.NetworkSecurityTrustManager");
        NSTM.checkServerTrusted.overload(
"[Ljava.security.cert.X509Certificate;""java.lang.String"
        ).implementation = function () {
console.log("[+] NetworkSecurityTrustManager bypassed");
        };
    } catch (e) { }

// ========== 6. WebView ==========
var WebViewClient = Java.use("android.webkit.WebViewClient");
    WebViewClient.onReceivedSslError.implementation = function (view, handler, error{
console.log("[+] WebView SSL error bypassed");
        handler.proceed();
    };

// ========== 7. TrustKit ==========
try {
var PTM = Java.use(
"com.datatheorem.android.trustkit.pinning.PinningTrustManager"
        );
        PTM.checkServerTrusted.implementation = function () {
console.log("[+] TrustKit bypassed");
        };
    } catch (e) { }

console.log("[*] === Java Layer Bypass Complete ===");
});

// ========== 8. Native 层 (libssl.so / libboringssl.so / libttboringssl.so) ==========
var sslLibNames = ["libssl.so""libboringssl.so""libttboringssl.so"];
sslLibNames.forEach(function (libName{
var mod = Process.findModuleByName(libName);
if (!mod) return;

// 禁用验证模式(OpenSSL / BoringSSL 通用)
var addr = Module.findExportByName(libName, "SSL_CTX_set_verify");
if (addr) {
        Interceptor.attach(addr, {
onEnterfunction (args{
                args[1] = ptr(0x0); // SSL_VERIFY_NONE
                args[2] = ptr(0x0);
console.log("[+] " + libName + " SSL_CTX_set_verify -> NONE");
            }
        });
    }

// BoringSSL 特有:SSL_CTX_set_custom_verify
var customVerifyAddr = Module.findExportByName(libName, "SSL_CTX_set_custom_verify");
if (customVerifyAddr) {
        Interceptor.attach(customVerifyAddr, {
onEnterfunction (args{
if (!args[2].isNull()) {
                    args[2] = new NativeCallback(function (ssl, out_alert{
return0// ssl_verify_ok
                    }, "int", ["pointer""pointer"]);
console.log("[+] " + libName + " SSL_CTX_set_custom_verify -> ssl_verify_ok");
                }
            }
        });
    }

// BoringSSL 特有:SSL_CTX_set_cert_verify_callback
var certVerifyAddr = Module.findExportByName(libName, "SSL_CTX_set_cert_verify_callback");
if (certVerifyAddr) {
        Interceptor.attach(certVerifyAddr, {
onEnterfunction (args{
if (!args[1].isNull()) {
                    args[1] = new NativeCallback(function (storeCtx, arg{
return1;
                    }, "int", ["pointer""pointer"]);
console.log("[+] " + libName + " SSL_CTX_set_cert_verify_callback -> 1");
                }
            }
        });
    }

// 强制验证结果为通过
var addr2 = Module.findExportByName(libName, "SSL_get_verify_result");
if (addr2) {
        Interceptor.attach(addr2, {
onLeavefunction (retval{
                retval.replace(ptr(0x0)); // X509_V_OK = 0
            }
        });
    }
});

console.log("[*] === Native Layer Bypass Complete ===");

5.3 应用检测 Frida 并崩溃

部分高安全应用会主动检测 Frida 的存在并强制退出。常见的检测手段包括:

检测方式
原理
特征
端口扫描
扫描 Frida 默认监听端口 27042
connect()
 到 localhost:27042
/proc/self/maps 扫描
检查内存映射中是否有 frida-agent
读取 maps 搜索 "frida" 字符串
进程名检测
遍历进程列表查找 frida-server
读取 /proc/<pid>/cmdline
D-Bus 协议探测
Frida 使用 D-Bus 协议通信
发送 D-Bus AUTH 消息并检查响应
内联 Hook 检测
检查函数入口处的指令是否被修改
对比函数前几个字节与预期指令
pthread_create 监控
Frida 注入时会创建额外线程
监控新线程的创建

快速应对措施

# 方法 1:使用非默认端口启动 frida-server(躲避端口扫描)
./frida-server -l 0.0.0.0:31337 &
# 客户端连接时指定端口
frida -H 127.0.0.1:31337 -f <package_name> -l bypass.js

# 方法 2:重命名 frida-server 二进制(躲避进程名检测)
cp frida-server fs-16.x
./fs-16.x &

# 方法 3:使用 Frida Gadget 注入(不需要 frida-server 进程)
# 将 frida-gadget.so 重命名后嵌入到 APK 的 lib 目录
# 这种方式没有 frida-server 进程,可以躲避大部分进程检测

深入对抗:对于高级检测(如 maps 扫描、inline hook 检测),需要编写专门的反检测脚本。这是一个独立的议题,涉及 Hook open()read()strstr() 等系统调用来隐藏 Frida 痕迹。


六、实战排错流程

当 SSL Pinning 绕过脚本不生效时,不要盲目换方案——按以下决策树逐步排查,定位真正的问题:

SSL Pinning 绕过排错决策树

排查要点速查

阶段
检查点
命令/方法
Frida 注入
frida-server 是否运行
frida-ps -U
Frida 注入
版本是否匹配
frida --version
 vs frida-server --version
Hook 命中
控制台是否有 [*] 输出
观察 Frida 控制台
Hook 命中
类名是否被混淆
Hook 异常堆栈追溯
抓包解密
代理是否正确配置
浏览器访问 http://burp
抓包解密
CA 证书级别
确认为系统级(非用户级)
抓包解密
是否走系统代理
使用 iptables 透明代理
抓包解密
是否 mTLS
服务器日志 / 抓包看 ClientCertificate
崩溃闪退
Frida 检测
改端口 / 重命名 / Gadget 模式
崩溃闪退
Root 检测
Magisk Hide / Shamiko
崩溃闪退
完整性检测
spawn 模式更早注入

七、工具生态速查

工具
用途
使用方式
适用场景
objection
一键绕过 SSL Pinning
objection -g pkg explore
 -> android sslpinning disable
快速测试,覆盖常见实现
frida-multiple-unpinning
社区维护的通杀脚本
frida -U -f pkg -l unpinning.js
覆盖面最广的脚本
apk-mitm
自动化重打包绕过
npx apk-mitm app.apk
不需要 root/Frida
httptoolkit
集成抓包 + 自动绕过
图形化操作
最简单的全套方案
Magisk + MagiskTrustUserCerts
系统级 CA 安装
安装 Magisk 模块
Android 7.0+ 证书问题
PCAPdroid
无 root 流量捕获
配合 SOCKS 代理使用
无 root 设备抓包
ProxyDroid
全局强制代理
设置 SOCKS/HTTP 代理
不走系统代理的应用

八、总结

场景
推荐方案
绕过难度
常规应用,初次尝试
objection / 通杀脚本
★☆☆☆☆
通杀脚本无效
分析 pinning 方式,针对性 Hook
★★★☆☆
OkHttp 混淆后
异常堆栈定位 + 枚举类 Hook
★★★☆☆
Native 层 pinning(libssl.so)
Hook SSL_CTX_set_verify / SSL_get_verify_result
★★★★☆
独立 libboringssl.so
Hook SSL_CTX_set_custom_verify + SSL_CTX_set_cert_verify_callback
★★★★☆
字节跳动系(libttboringssl.so)
枚举导出符号 Hook + dlopen 监听 + 必要时逆向偏移
★★★★★
mTLS 双向认证
提取客户端证书 + SSL 绕过
★★★★☆
多重 pinning + 反调试
组合绕过 + 反检测
★★★★★

核心原则:无论 SSL Pinning 实现多复杂,最终都要经过 TLS 握手。找到验证入口点,让它返回"通过",就是所有绕过方案的本质。

推荐的实战顺序:

  1. 先试通杀脚本——80% 的应用可以直接搞定
  2. 分析失败原因——通过异常堆栈、Logcat、Frida 日志定位
  3. 针对性 Hook——根据具体实现编写精确的绕过脚本
  4. 处理反调试——如果应用检测 Frida,先绕过检测再绕过 Pinning

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-04-21 20:30:58 HTTP/2.0 GET : https://c.mffb.com.cn/a/479973.html
  2. 运行时间 : 0.081076s [ 吞吐率:12.33req/s ] 内存消耗:4,532.43kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=40bf21b39a40bfbfd1496bcd6e90a75d
  1. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/runtime/temp/cefbf809ba1a84190cb04b0cb7abcf79.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/c.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000657s ] mysql:host=127.0.0.1;port=3306;dbname=c_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000697s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000323s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000246s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000489s ]
  6. SELECT * FROM `set` [ RunTime:0.000207s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000517s ]
  8. SELECT * FROM `article` WHERE `id` = 479973 LIMIT 1 [ RunTime:0.000547s ]
  9. UPDATE `article` SET `lasttime` = 1776774658 WHERE `id` = 479973 [ RunTime:0.002856s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.000241s ]
  11. SELECT * FROM `article` WHERE `id` < 479973 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000444s ]
  12. SELECT * FROM `article` WHERE `id` > 479973 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000374s ]
  13. SELECT * FROM `article` WHERE `id` < 479973 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000683s ]
  14. SELECT * FROM `article` WHERE `id` < 479973 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.001553s ]
  15. SELECT * FROM `article` WHERE `id` < 479973 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.000976s ]
0.082707s