14、Frida绕过证书绑定:Frida框架介绍、Frida Hook技术、Frida绕过证书绑定脚本

好,咱们今天聊点硬核的。前面几章我们把证书绑定的原理和实现方式都捋了一遍,也讲了怎么用Xposed去Hook它。但说实话,在真实的Android逆向战场上,Frida才是我的主力武器。

为什么?因为Frida不需要重启手机,不需要修改APK,甚至不需要Root权限(某些场景下)。你写一段JavaScript脚本,就能动态地注入到目标进程里,想Hook哪个函数就Hook哪个函数。这感觉,就像你拿着一把手术刀,直接对运行中的App做「开胸手术」。

我个人习惯把Frida比作「移动端的调试器Pro Max版」。它比Xposed更灵活,比直接改Smali更高效。今天我们就来彻底搞懂它,并且用它干掉证书绑定。

14.1 Frida框架介绍

Frida,全称是「Frida Dynamic Instrumentation Toolkit」。说白了,它是一个动态插桩工具。你不需要提前修改App的代码,而是在App运行的时候,把我们的代码「注射」进去。

它的核心架构是这样的:

Frida 核心架构图 PC端(你的电脑) frida-tools / frida CLI / Python绑定 USB / TCP 连接 Android设备(手机/模拟器) frida-server(守护进程) 注入JS代码 目标App进程 Hook函数 / 替换返回值 / 调用Java方法

你看,整个流程很清晰。你在电脑上写JavaScript脚本,通过USB或者TCP把脚本发给手机上的frida-server,frida-server再把脚本注入到目标App的进程里。然后你的脚本就可以在App内部为所欲为了。

核心要点:Frida的工作模式是C/S架构。电脑是客户端,手机是服务端。服务端(frida-server)负责注入,客户端(frida-tools)负责发送脚本和接收结果。

我记得我第一次用Frida的时候,最震撼的一点是:不需要重启App。你写好了脚本,保存,然后在命令行里敲一下回车,脚本就生效了。如果写错了,改一下,再敲回车,新的脚本又进去了。这种「热更新」式的调试体验,比Xposed那种改完代码还要重启手机的方式,不知道高到哪里去了。

14.2 Frida Hook技术

Frida的Hook技术,说白了就两种:Java层HookNative层Hook。我们做证书绑定绕过,99%的情况都是在Java层动手脚。因为大部分App的证书校验逻辑,都是用Java写的。

14.2.1 Java层Hook基础

Frida的Java层Hook,用的是JavaScript语法。你可能会问:「我一个搞Android的,还得学JavaScript?」别担心,Frida的JS API非常简洁,你花10分钟就能上手。

来看一个最基础的例子:

// 1. 找到要Hook的类
var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');

// 2. 找到要Hook的方法
TrustManagerImpl.checkServerTrusted.implementation = function(certs, authType) {
    console.log('[+] 绕过证书校验!');
    // 直接返回,不进行任何校验
    return;
};

这段代码干了什么?它找到了Android系统里负责证书校验的类TrustManagerImpl,然后把它的checkServerTrusted

小技巧:如果你不确定目标App用的是哪个类来做证书校验,可以先用Frida的Java.enumerateLoadedClasses()枚举所有已加载的类,然后过滤出包含"TrustManager"、"SSL"、"Certificate"等关键字的类。我在项目中经常用这招来快速定位目标。

14.2.2 常用Hook模式

在实际工作中,我总结了几种最常用的Hook模式:

模式 适用场景 代码示例
替换实现 直接跳过校验逻辑 method.implementation = function() { return; }
修改参数 篡改传入的参数值 method.implementation = function(a, b) { a = 'hacked'; return this.method(a, b); }
修改返回值 伪造返回结果 method.implementation = function() { return true; }
调用原方法 在原始逻辑前后插入代码 method.implementation = function() { console.log('before'); var ret = this.method(); console.log('after'); return ret; }

你想想看,有了这四种模式,基本上你能想到的任何Hook需求都能满足了。特别是「调用原方法」这个模式,我经常用它来打印函数的入参和返回值,用来分析App的通信协议。

14.2.3 动态加载与时机问题

这里有个坑,我必须提醒你。Frida的Hook脚本是在App启动之后才注入的。如果App在启动的瞬间就完成了证书校验,你的脚本可能还没来得及Hook上去,校验就已经结束了。

我曾经在一个金融App上遇到过这个问题。App在Application的onCreate里就做了证书绑定,我的脚本还没加载完,它就已经校验失败了。怎么办?

解决方案是:使用Frida的延迟加载机制。你可以让脚本等待目标类加载完成后再进行Hook:

// 等待类加载完成
Java.perform(function() {
    // 延迟100ms,确保类已经加载
    setTimeout(function() {
        var TargetClass = Java.use('com.example.MyTrustManager');
        TargetClass.checkServerTrusted.implementation = function() {
            console.log('[+] 延迟Hook成功!');
            return;
        };
    }, 100);
});

或者更稳妥的做法,是用Java.enumerateLoadedClasses配合回调,等目标类出现时再Hook。嗯,这里要注意,时机问题是Frida使用中最容易翻车的地方,没有之一。

14.3 Frida绕过证书绑定脚本

好了,理论知识讲完了。咱们直接上干货——一个完整的、经过实战检验的Frida绕过证书绑定脚本。

这个脚本我用了很久,从Android 6到Android 14,从OkHttp到HttpsURLConnection,基本上通吃。它的核心思路是:Hook所有可能做证书校验的地方,一个都不放过

// 通用Frida绕过证书绑定脚本
// 作者:资深移动安全工程师
// 适用:Android 6 - 14,OkHttp / HttpsURLConnection / WebView

Java.perform(function() {
    console.log('[+] 开始绕过证书绑定...');

    // ========== 1. 绕过 TrustManager 校验 ==========
    try {
        var TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl');
        TrustManagerImpl.checkServerTrusted.implementation = function(certs, authType) {
            console.log('[+] 绕过 TrustManagerImpl.checkServerTrusted');
            return;
        };
        console.log('[✓] TrustManagerImpl 已Hook');
    } catch(e) {
        console.log('[-] TrustManagerImpl 未找到: ' + e);
    }

    // ========== 2. 绕过 X509TrustManager 校验 ==========
    try {
        var X509TrustManager = Java.use('javax.net.ssl.X509TrustManager');
        X509TrustManager.checkServerTrusted.implementation = function(chain, authType) {
            console.log('[+] 绕过 X509TrustManager.checkServerTrusted');
            return;
        };
        X509TrustManager.checkClientTrusted.implementation = function(chain, authType) {
            console.log('[+] 绕过 X509TrustManager.checkClientTrusted');
            return;
        };
        console.log('[✓] X509TrustManager 已Hook');
    } catch(e) {
        console.log('[-] X509TrustManager 未找到: ' + e);
    }

    // ========== 3. 绕过 HostnameVerifier 校验 ==========
    try {
        var HostnameVerifier = Java.use('javax.net.ssl.HostnameVerifier');
        HostnameVerifier.verify.implementation = function(hostname, session) {
            console.log('[+] 绕过 HostnameVerifier.verify: ' + hostname);
            return true;
        };
        console.log('[✓] HostnameVerifier 已Hook');
    } catch(e) {
        console.log('[-] HostnameVerifier 未找到: ' + e);
    }

    // ========== 4. 绕过 OkHttp 的 CertificatePinner ==========
    try {
        var CertificatePinner = Java.use('okhttp3.CertificatePinner');
        CertificatePinner.check.implementation = function(hostname, peerCertificates) {
            console.log('[+] 绕过 OkHttp CertificatePinner: ' + hostname);
            return;
        };
        console.log('[✓] OkHttp CertificatePinner 已Hook');
    } catch(e) {
        console.log('[-] OkHttp CertificatePinner 未找到: ' + e);
    }

    // ========== 5. 绕过 WebView 的 SSL 校验 ==========
    try {
        var WebViewClient = Java.use('android.webkit.WebViewClient');
        WebViewClient.onReceivedSslError.implementation = function(view, handler, error) {
            console.log('[+] 绕过 WebView SSL错误: ' + error);
            handler.proceed();  // 继续加载,忽略错误
        };
        console.log('[✓] WebView SSL 已Hook');
    } catch(e) {
        console.log('[-] WebView SSL 未找到: ' + e);
    }

    console.log('[✓] 所有证书绑定绕过完成!');
});

重要警告:这个脚本仅供安全研究和授权测试使用。未经授权对他人App进行抓包和逆向,是违法行为。我曾经见过有人因为用这个脚本抓取竞品数据,最后吃了官司。请务必在法律允许的范围内使用。

14.3.1 脚本使用方式

把这个脚本保存为bypass_ssl.js,然后在命令行里执行:

# 1. 启动frida-server(在手机上)
adb shell su -c '/data/local/tmp/frida-server &'

# 2. 运行脚本(在电脑上)
frida -U -f com.target.app -l bypass_ssl.js --no-pause

参数说明:

  • -U:连接USB设备
  • -f com.target.app:启动目标App的包名
  • -l bypass_ssl.js:加载我们的脚本
  • --no-pause:不暂停,直接运行

14.3.2 脚本设计思路

你可能会问:「为什么要Hook这么多地方?只Hook一个TrustManager不行吗?」

答案是:不行。不同的App用了不同的网络库。有的用OkHttp,有的用原生的HttpsURLConnection,有的用WebView。甚至同一个App里,不同的网络请求可能走了不同的校验路径。

我记得有一次,我帮一个朋友分析某个社交App。我Hook了TrustManager,抓包工具能抓到登录接口的数据了。但到了图片上传接口,死活抓不到。后来才发现,图片上传用的是OkHttp的CertificatePinner,我根本没Hook它。加上之后,一切正常。

所以,我的经验是:宁可多Hook,不可漏Hook。把所有可能做证书校验的地方全部覆盖,这样不管App走哪条路,都会被我们拦截。

14.4 常见问题与避坑指南

最后,分享几个我在实战中踩过的坑,希望能帮你少走弯路。

避坑指南:

  • 问题1:脚本注入成功,但抓包工具还是看不到流量。
    原因:App可能用了Native层的证书校验,比如用C++写的OpenSSL。解决方案:需要Hook Native层的SSL_read/SSL_write函数,这个我们后面会讲。
  • 问题2:Hook了TrustManager,但App直接崩溃。
    原因:有些App会检测TrustManager是否被篡改。解决方案:用Frida的Java.choose动态查找实例,而不是用Java.use静态Hook。
  • 问题3:脚本在Android 14上不工作。
    原因:Android 14加强了运行时权限检查。解决方案:确保frida-server版本是最新的(15.2.0以上),并且使用--codeshare参数。

我曾经在一个Android 14的设备上折腾了整整两天,就是因为frida-server版本太旧。升级到最新版后,问题迎刃而解。所以,保持工具版本更新,是安全工程师的基本素养。

好了,关于Frida绕过证书绑定,我们就聊到这里。你把这个脚本保存好,以后遇到大部分App的证书绑定,都能用得上。记住,工具是死的,思路是活的。理解了Frida的Hook原理,你就能举一反三,应对各种奇奇怪怪的校验逻辑。


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

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