SSL Pinning绕过:证书锁定原理、Frida Objection一键绕过、Xposed模块绕过

说到SSL Pinning,也就是证书锁定,这玩意儿在移动安全里算是个老生常谈的话题了。但我发现很多刚入行的朋友,要么觉得它高深莫测,要么觉得随便装个JustTrustMe就能搞定一切。嗯,这两种想法都有点偏。

我个人习惯把SSL Pinning理解成「应用自己给自己发了一张内部通行证」。正常来说,你的App访问HTTPS服务器,系统会去校验服务器证书是不是由可信CA签发的。但SSL Pinning不一样——它把服务器证书或者公钥直接硬编码在App里,只认这一个。说白了,就算你系统里装了根证书,中间人代理的证书它也不认。

我在项目中遇到过不少次,明明Charles抓包配置好了,结果App直接报错「网络异常」或者「证书验证失败」。这时候你就知道,SSL Pinning在作怪了。

证书锁定的原理

先简单过一下原理,不然你连对手长什么样都不知道,怎么打?

SSL Pinning的实现方式主要有两种:

  • 证书锁定(Certificate Pinning):直接比对服务器返回的证书内容,跟本地硬编码的证书是否一致。这种方式比较严格,一旦服务器换证书,App就得跟着更新。
  • 公钥锁定(Public Key Pinning):只比对证书里的公钥。好处是服务器换证书但公钥不变的话,App不用改。我个人更推荐这种方式,灵活一些。

在Android端,常见的实现手段包括:

  • OkHttp的CertificatePinner
  • 自定义X509TrustManager
  • WebView的onReceivedSslError回调
  • 第三方库如TrustKit

你想想看,这些实现方式虽然代码写法不同,但核心逻辑都一样——在SSL握手阶段,多了一层自定义校验。校验不通过,直接抛异常或者断开连接。

核心要点:SSL Pinning的本质是「应用层自己管证书校验」,绕过的思路就是「让应用层放弃校验,或者让校验永远通过」。

Frida Objection一键绕过

好,原理讲完了,咱们直接上实战。我个人最常用的就是Frida配合Objection,因为真的快。

Objection是一个基于Frida的运行时探索工具,它内置了很多绕过SSL Pinning的脚本。你不需要自己写Hook代码,一条命令就搞定。

具体操作步骤:

  1. 确保手机已root,并且安装了frida-server
  2. 电脑端安装Objection:pip install objection
  3. 启动App并注入Objection:
objection -g com.example.app explore

进入Objection交互界面后,直接输入:

android sslpinning disable

就这么简单。Objection会自动Hook掉常见的SSL Pinning实现,包括OkHttp、HttpURLConnection、WebView等。我在项目里测试过,大概80%的App都能用这招直接绕过。

小技巧:如果android sslpinning disable没效果,可以试试android hooking list classes先看看App里有没有自定义的TrustManager类,然后手动Hook。

为什么会这样?因为Objection背后做的事情,其实就是Frida Hook了javax.net.ssl.SSLContextorg.apache.http.conn.ssl这些关键类,让它们永远返回「证书验证通过」的结果。

我记得有一次,一个金融类App用了自定义的证书校验逻辑,Objection的默认脚本没生效。我当时用Frida手动Hook了它的checkServerTrusted方法,直接让这个方法空实现,问题就解决了。

Java.perform(function() {
    var TrustManager = Java.use('javax.net.ssl.X509TrustManager');
    TrustManager.checkServerTrusted.implementation = function(chain, authType) {
        console.log('[+] Bypassed SSL Pinning');
    };
});

这段代码虽然简单,但很实用。你把它保存成ssl_bypass.js,然后用frida -U -f com.example.app -l ssl_bypass.js运行就行。

Xposed模块绕过

说完Frida,再聊聊Xposed。虽然现在Android高版本对Xposed的支持不太好,但在一些老设备或者模拟器上,Xposed依然是个好选择。

Xposed绕过SSL Pinning的原理跟Frida类似,都是Hook关键方法。但Xposed是持久化的,Hook一次,重启App依然生效。Frida是动态注入的,每次都要重新跑。

常用的Xposed模块有:

  • JustTrustMe:最经典的模块,一键绕过。但说实话,它已经有点老了,很多新App的校验方式它覆盖不到。
  • SSLUnpinning:支持更多场景,包括WebView和自定义TrustManager。
  • TrustMeAlready:这个模块我比较喜欢,因为它不仅绕过SSL Pinning,还能绕过证书透明度校验。

安装Xposed模块的步骤我就不细说了,大家应该都会。重点说一下,如果这些现成模块都不管用怎么办?

我曾经遇到过一个App,它把证书校验逻辑写在了Native层,用JNI调用的。那时候JustTrustMe和SSLUnpinning全都没用。怎么办?自己写Xposed模块呗。

自己写Xposed模块其实不复杂,核心就是找到目标类和方法,然后Hook。比如你要Hook一个自定义的MyTrustManager

public class MyTrustManager implements X509TrustManager {
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) 
            throws CertificateException {
        // 这里做了证书校验
    }
}

你的Xposed模块可以这样写:

XposedHelpers.findAndHookMethod(
    "com.example.MyTrustManager",
    lpparam.classLoader,
    "checkServerTrusted",
    X509Certificate[].class,
    String.class,
    new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            // 直接返回,不执行原方法
            param.setResult(null);
        }
    }
);

嗯,这里要注意一点:如果原方法抛出了CertificateException,你直接setResult(null)会导致调用方收到null返回值,可能会引发空指针。更好的做法是让原方法正常执行,但把校验逻辑改成永远通过。

避坑指南:我曾经因为直接setResult(null)导致App崩溃,排查了半天才发现是调用方对返回值做了非空判断。后来我改成param.setResult(null)配合XposedBridge.log打印日志,才定位到问题。建议大家在Hook时,先打印日志确认方法被调用了,再决定怎么处理返回值。

三种方式的对比

说了这么多,我整理了一个表格,方便你对比选择:

方式 优点 缺点 适用场景
Frida Objection 一键操作,无需重启,支持动态调试 需要每次注入,对高版本Android兼容性一般 快速验证、临时绕过
Frida 手动Hook 灵活,能处理自定义校验逻辑 需要写代码,对新手不友好 Objection失效时、复杂场景
Xposed模块 持久化,一次安装长期有效 需要root,高版本Android支持差 老设备、模拟器、长期测试

知识体系结构图

下面这张图,我把SSL Pinning绕过的核心逻辑和工具链梳理了一下,方便你建立整体认知:

SSL Pinning 绕过知识体系 SSL Pinning 绕过 证书锁定原理 证书锁定 vs 公钥锁定 OkHttp / 自定义TrustManager Frida / Objection 一键绕过:android sslpinning disable 手动Hook:checkServerTrusted Xposed 模块 JustTrustMe / SSLUnpinning 自定义Xposed模块Hook 核心思路:让证书校验永远返回「通过」 工具选型:快速验证用Objection,复杂场景用Frida手动Hook,持久化用Xposed

写在最后

SSL Pinning绕过,说白了就是「你锁你的,我绕我的」。原理不复杂,但实际项目中总会遇到各种奇葩情况。比如App用了双向SSL认证,或者把证书校验逻辑写在了Native层,甚至用VMP保护了起来。这时候,上面的方法可能就不够用了。

我个人建议,先从Objection的一键命令开始试,不行再上Frida手动Hook,最后才考虑Xposed。毕竟,能用简单方法解决的问题,没必要搞复杂。

嗯,今天就聊到这儿。如果你在实际操作中遇到什么奇怪的问题,欢迎来交流。


公众号:蓝海资料掘金营,微信deep3321