33、网络协议逆向(二):HTTP/HTTPS协议逆向、SSL Pinning绕过

说实话,HTTP/HTTPS协议逆向是逆向工程里最常碰到的活儿。你想想看,现在哪个App不联网?只要联网,十有八九走的就是HTTP或HTTPS。我这些年做过的逆向项目里,至少有一半的时间都在跟这些协议打交道。

这一讲,咱们就聊聊怎么逆向HTTP/HTTPS协议,以及怎么绕过SSL Pinning——说白了,就是怎么让App乖乖地把加密内容吐出来。

HTTP协议逆向:基础但关键

HTTP协议本身是明文的,逆向起来相对简单。但别小看它,很多App虽然用了HTTPS,但在某些场景下还是会回退到HTTP——比如资源文件、图片、或者某些不敏感的数据请求。

我个人的习惯是,先抓HTTP的包看看。有时候你会发现一些意想不到的东西。

抓包工具的选择

常用的抓包工具就那几个:

  • Burp Suite:功能最全,适合深度分析
  • Fiddler:Windows上顺手,脚本扩展能力强
  • Charles:Mac用户的最爱,界面简洁
  • Wireshark:底层抓包,适合分析TCP层问题

我个人偏爱Burp Suite。为什么?因为它对请求修改、重放的支持做得最好。你想想看,逆向协议的时候,经常需要改几个参数看看服务器怎么回应,Burp的Repeater功能简直就是为这个场景设计的。

HTTP请求的结构分析

一个典型的HTTP请求长这样:

POST /api/v1/login HTTP/1.1
Host: app.example.com
Content-Type: application/json
User-Agent: MyApp/1.0 (Android 12; Pixel 6)
X-Request-ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...

{"username":"test","password":"123456"}

逆向的时候,我通常会关注这几个点:

  • 请求头:有没有自定义的Header?比如X-Request-ID这种,可能是防重放攻击的
  • 请求体:参数是怎么组织的?JSON?Form?还是Protobuf?
  • Cookie:Session是怎么管理的?有没有HttpOnly标记?
  • User-Agent:有时候App会通过UA来区分版本,甚至做A/B测试
小技巧:如果看到请求里有时间戳参数,比如timestamp=1680000000,试着改一下这个值。我曾经遇到过某个App,时间戳偏差超过5分钟就直接返回403——这就是典型的防重放机制。

HTTPS协议逆向:真正的挑战

HTTPS比HTTP麻烦多了。因为数据是加密的,你抓到的包全是乱码。要解密HTTPS流量,核心思路就一个——让客户端信任你的CA证书

具体怎么做?有两种主流方式:

方式一:安装代理证书(最简单)

这是最常用的方法。流程如下:

  1. 在抓包工具(比如Burp)里导出CA证书
  2. 把证书安装到手机或模拟器上
  3. 设置代理,让流量走抓包工具
  4. 抓包工具用自己的证书解密HTTPS流量

听起来简单对吧?但这里有个坑——Android 7+ 默认不信任用户安装的证书。也就是说,你装了自己的CA证书,App照样不认。

注意:Android 7及以上版本,只有系统证书才被信任。用户安装的证书对targetSdkVersion >= 24的App无效。解决办法是root手机,然后把证书放到系统证书目录(/system/etc/security/cacerts/)。

方式二:Hook SSL库(更通用)

如果方式一搞不定,那就得上Hook了。常用的工具有:

  • Frida:动态插桩,可以Hook Java层的SSLContext
  • Xposed:框架级Hook,适合系统层面的修改
  • JustTrustMe:一个Xposed模块,专门用来绕过SSL Pinning

我个人更推荐Frida。为什么?因为它不需要重启手机,热插拔式的Hook,调试效率高很多。

SSL Pinning绕过:硬骨头怎么啃

SSL Pinning,说白了就是App在代码里写死了服务器的证书或公钥。即使你装了代理证书,App发现证书对不上,直接拒绝连接。

我遇到过最狠的一个App,它不光做了证书Pinning,还做了公钥Pinning,甚至把证书哈希值写在了native层。嗯,那一次确实折腾了我两天。

绕过方法一:Hook证书校验函数

最直接的方法,就是Hook掉证书校验的逻辑。以Android为例,常见的Hook点有:

// Java层Hook示例(Frida脚本)
Java.perform(function() {
    var SSLContext = Java.use('javax.net.ssl.SSLContext');
    SSLContext.init.implementation = function(km, tm, sr) {
        console.log('SSLContext.init called');
        // 传入空的TrustManager,信任所有证书
        this.init(km, null, sr);
    };
});

这段代码干了什么?它Hook了SSLContext的init方法,把TrustManager换成了空的。这样一来,任何证书都会被信任。

核心思路:找到App校验证书的地方,然后让它「睁一只眼闭一只眼」。

绕过方法二:替换Pinning逻辑

有些App用了OkHttp的CertificatePinner,代码里写死了证书哈希值:

// 原始代码
CertificatePinner pinner = new CertificatePinner.Builder()
    .add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
    .build();

绕过方法也很简单——Hook掉CertificatePinner的check方法,让它什么都不做:

// Frida Hook OkHttp CertificatePinner
Java.perform(function() {
    var CertificatePinner = Java.use('okhttp3.CertificatePinner');
    CertificatePinner.check.implementation = function(host, peerCertificates) {
        console.log('Bypassing SSL Pinning for: ' + host);
        // 直接返回,不校验
        return;
    };
});

绕过方法三:Native层Pinning

这是最头疼的情况。App把证书校验逻辑写在了C/C++层,Java层根本看不到。遇到这种情况,我一般会:

  1. 先用Frida的Stalker跟踪native函数调用
  2. 找到SSL_read/SSL_write等OpenSSL函数
  3. Hook这些函数,直接修改返回值

说实话,native层的Pinning没有通用解法。每个App的实现都不一样,只能具体问题具体分析。

知识体系总览

下面这张图,是我整理的本章节核心知识结构:

HTTP/HTTPS协议逆向 & SSL Pinning绕过 知识体系 网络协议逆向 HTTP协议逆向(明文) 请求头/请求体分析 Cookie/Session管理 自定义Header识别 HTTPS协议逆向(加密) 安装代理证书 Hook SSL库 SSL Pinning绕过 Java层 / Native层

实战中的避坑指南

做了这么多年逆向,踩过的坑不少。分享几个典型的:

  • 证书安装后还是抓不到包:检查一下代理设置对不对。我曾经花了半小时才发现是VPN和代理冲突了。
  • Hook脚本不生效:先确认App有没有做反调试。有些App会检测Frida,检测到了直接退出。
  • 抓到的包是乱码:看看是不是用了自定义加密。有些App在HTTPS之上又套了一层加密,比如AES加密请求体。
  • App闪退:可能是证书校验失败导致的crash。试试在Hook里加个try-catch,别让异常抛出去。
重要提醒:SSL Pinning绕过只适用于你拥有合法权限的App。未经授权绕过别人的安全机制,可能涉及法律问题。咱们做逆向的,技术归技术,底线还是要有的。

总结

HTTP/HTTPS协议逆向,说白了就是两件事:抓到包解出密。抓包靠工具,解密靠Hook。SSL Pinning是App的最后一道防线,但也不是无解的——Java层的Pinning用Frida几行脚本就能绕过,native层的虽然麻烦,但只要耐心分析,总能找到突破口。

我个人觉得,逆向协议最关键的还是思路。工具和脚本都是死的,但你的脑子是活的。遇到一个没见过的协议,先别急着上Hook,静下心来分析一下流量特征,往往能发现一些规律。

好了,这一讲就到这里。记住,多动手,多踩坑,经验就是这么积累起来的。


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