安卓抓包与加解密对抗

0x01 前言

  • Web测试成熟,App测试受限: Web测试工具(Burp Suite,Yakit等)成熟,断点调试方便。然而,App测试因HTTPS抓包以及加密参数难题而难以深入。
  • 核心挑战:HTTPS中间人防护: App开发者广泛采用多种技术阻止HTTPS流量被安全测试工具(如Burp Suite, Yakit)拦截分析。加解密对抗:请求参数和响应体加密。

0x02 HTTPS原理

要解决各种抓包问题,burpsuite/yakit 实现https的抓包原理要简单了解一下,可以更好地理解各种抓包解决方案。

1.标准https流程

image-20250618101651566

  • 客户端发起连接请求 (ClientHello)。
  • 服务器响应,发送其数字证书 (ServerHello, Certificate)。
  • 客户端验证服务器证书(有效性、是否由信任CA签发、域名匹配等)。
  • 验证通过后,客户端生成密钥,用服务器证书中的公钥加密后发送 (ClientKeyExchange)。
  • 服务器用其私钥解密获得密钥。
  • 后续通信使用该密钥进行对称加密(AES等)保障机密性和完整性。

2.中间人攻击的https流程

image-20250618104027556

  • 客户端发起连接请求 (ClientHello)。
  • burpsuite 截获客户端请求,然后伪装成客户端与服务器进行通信。
  • 服务器响应,发送其数字证书 (ServerHello, Certificate)。
  • burpsuite获取到服务器发送的证书,然后自己生成公私钥,并将公钥发送给客户端。
  • 客户端验证Burpsuite证书(有效性、是否由信任CA签发、域名匹配等)。
  • 验证通过后,客户端生成密钥,用burpsuite证书中的公钥加密后发送 (ClientKeyExchange)。
  • burpsuite使用私钥解密后,获得密钥,然后使用服务器公钥加密密钥,发送给服务器
  • 服务器用其私钥解密获得密钥。
  • 完成这一流程之后,客户端发送给服务器的内容,在BurpSuite上解密得到明文,所以我们在BurpSuite上看到的内容就是明文。而向服务器发送请求的时候,BurpSuite会用服务器协商好的密钥进行加密然后传输内容。
  • 同样服务器返回的内容也是,BurpSuite会先解密,然后得到明文, BurpSuite 会伪装成服务器,再用BurpSuite与客户端协商好的密钥进行加密然后传输内容。
  • 通过这样的手段,便可以获取客户端和服务器之间通信的所有内容。使用中间人攻击手段,必须要让客户端信任中间人的证书,如果客户端不信任,则这种攻击手段也无法发挥作用。

3.抓包分析

这里我用wireshark抓取了一下https握手的整个过程,可以简单的看到tcp三次握手,然后tls握手

image-20250619160023223

  • Client Hello

    客户端发起连接

  • Server Hello

    服务器响应客户端,确定本次使用的tls版本和加密套件等

  • Certificate,Server Key Exchange,Server Hello Done

    • 服务器发送其数字证书链,用于身份验证。客户端将验证证书的有效性(是否过期、是否由可信CA签发、域名匹配等)。
    • 当使用非RSA密钥交换算法(如ECDHE、DHE)时发送。包含服务器的临时公钥参数(如椭圆曲线参数、DH参数)和签名。
    • 表示服务器握手消息发送完毕,等待客户端响应。
  • Client key Exchange,Change Cipher Spec,Encrypted Handshake Message

    • 客户端生成预主密钥(Pre-Master Secret),用服务器证书中的公钥加密后发送(RSA算法)或发送客户端的临时公钥参数(ECDHE/DHE)。
    • 客户端通知服务器:“后续通信将使用协商的密钥和算法进行加密”。
    • 客户端使用协商的密钥和算法加密的第一条消息,实际是Finished消息(包含所有握手消息的摘要,用于验证握手完整性)。
  • Change Cipher Spec,Encrypted Handshake Message

    • 服务器通知客户端:“我已准备好切换至加密通信”。
    • 服务器发送的加密Finished消息(验证握手完整性)。
  • Application Data

    握手完成后,双方开始传输加密的应用层数据(如HTTP请求/响应)。

0x03 防抓包对抗策略

1.代理检测

  • 原理:app检测系统是否使用了HTTP/HTTPS代理。检测到代理则拒绝发送请求
  • 对抗方案:可以使用VPN隧道代理,使用 Postern (Android) 或 Surfboard 等工具,建立VPN隧道,将所有流量(TCP/UDP)路由到指定的SOCKS5或HTTP代理(如Burp/Yakit)。App无法通过常规API检测到这种“全局代理”。

2.SSL Pinning

  • 原理:App预先存储了其信任的服务器证书信息。在HTTPS握手时,不再仅依赖系统信任库(CA链),而是将收到的服务器证书信息与预存信息进行比对。不匹配则终止连接,即使证书由Burp CA签发且系统信任了Burp CA。

  • 主要类型

    • 证书绑定

      • 客户端预存服务器证书的完整副本
      • 验证时直接对比与服务器返回的证书是否一致
      • 缺点:证书到期需要更新应用
    • 公钥绑定

      • 客户端存储服务器证书的hash值
      • 验证时提取服务器的公钥并计算hash值,并与客户端存储的hash比对是否一致
      • 优势:证书可轮换,只要公钥不变则无需更新应用
    • hash绑定

      • 与公钥绑定一致,但绑定的是证书整体hash值或者特定字段的hash值
  • 对抗方案:

    • 运行时Hook:使用Frida或Xposed框架,hook证书校验的关键函数,强制返回验证成功
      • JustTrustMe (Xposed): 经典模块,Hook常见HTTP库(OkHttp, HttpClient, HttpURLConnection, WebViewClient)的证书验证逻辑。需Root+Xposed环境。
      • Frida脚本: 更灵活(需Frida Server)。编写脚本Hook TrustManager, X509TrustManager.checkServerTrusted(), OkHttpCertificatePinner 或特定App的验证函数,修改其返回值或逻辑。
    • 修改APK:
      • 反编译apk,定位对应的证书验证代码,移除验证逻辑或修改为始终返回成功,然后重新打包并签名。

3.双向认证

  • 原理:服务端在发送自己的证书时,也请求客户端发送客户端的证书

    • 服务器在发送自己证书的同时,发送一个 CertificateRequest 消息,要求客户端提供其证书。
    • 客户端必须提供其客户端证书(通常包含在App中或由服务器动态下发)及对应的私钥
    • 服务器验证客户端证书(是否由其信任的CA签发、是否有效、是否在黑名单等)。
    • 只有客户端证书验证通过,服务器才会继续握手并建立连接。

    双向认证

    image-20250619134745209

  • 对抗方案:

    • 反编译提取证书+Hook获取密钥
      • 提取证书:在apk资源文件中或者反编译代码中搜索包含证书的文件,或者硬编码的字符串。
      • 提取私钥:私钥通常不会明文存储,需要在运行时提取
        • 使用frida, hook Android KeyStore API (KeyStore.getEntry, PrivateKey.getEncoded) 或App加载/使用私钥的特定函数,在私钥被加载到内存时dump其字节码(通常为PKCS#8格式)
        • 内存dump:使用r0capture或者frida-dump等工具dump App进程内存,搜索可能的私钥特征(PKCS#8 Header -----BEGIN PRIVATE KEY----- 或 ASN.1结构),需结合分析。
    • 使用r0capture抓包
      • r0capture 是一个强大的基于Frida的抓包工具,专门设计用于对抗双向认证、代理检测、SSL Pinning等。
      • 它通过Frida Hook,在App内部拦截明文的HTTP/HTTPS请求和响应,直接输出到控制台或文件,完全绕过底层的TLS/SSL加密和验证过程

0x04 加解密对抗

解决了抓包,常面临请求参数/响应体的加密/签名,需逆向分析App的加解密逻辑。

  • 核心挑战

    • 代码混淆:类名、方法名、变量名被混淆,增加阅读难度
    • 分层实现
      • Java层:相对容易分析(Jadx-GUI)
      • Native层:编译为.so库,涉及汇编(IDA Pro)性能要求高或者核心算法通常会在Native层
      • 跨平台框架:Flutter(Dart编译为Native),可以使用特定工具分析
  • 基本思路

    1. 定位关键点:通过抓包观察(哪些数据加密?加密模式?)、搜索特征字符串(算法名如AES、RSA、SM2、SM4、Sign)、Hook网络库入口/出口分析数据变化
    2. 静态分析:使用反编译工具(Jadx、IDA Pro),理解算法、密钥来源、模式(CBC/ECB/GCM?)、填充(PKCS#7?)、IV/Nonce生成等。
    3. 动态调试:使用 Fridalldb/gdb Hook关键函数(加解密函数,密钥加载函数,随机数生成函数),动态获取输入参数、输出接口、密钥、IV。用于验证静态分析的结论。
    4. 算法还原/模拟:根据分析结果,用python/java等语言还原加解密/签名算法,用于测试脚本或自动化

0x05 实战举例

这里以某App举例,使用到的工具有

  • Yakit
  • ida pro
  • frida
  • blutter
  • postern
  • adb

该app 使用的 flutter 框架,使用的自定义的网络协议,不走系统代理,所以在开启wifi 代理后,虽然可以正常发起http请求,但是burp/yakit是抓不到数据包的,这里使用的vpn代理的方式,使用的工具是 postern

这里放一张工具截图

然后在yakit中设置代理认证

image-20250617112854581

即可抓取到数据包

image-20250619160412826

解决了抓包问题,这里还要解决请求参数的问题,而安卓app不能像web应用一样通过浏览器断点调试的方式,获取到对应的参数,这就需要利用逆向和动态调试去分析

flutter应用主要关注 /app-name/lib/arm64-v8a目录下的.so文件,如果有libflutter.solibapp.se文件,则证明该app使用的flutter框架,

libapp.so中是编译后的业务代码,所有的Dart逻辑(UI组件,网络请求,加密算法)均编译为AOT机器码存储在libapp.so中,这里需要逆向加解密算法,所以只需要反编译这个.so文件即可

image-20250616143559157

直接使用ida pro 来分析这个 libapp.so,可以看到全部都是混淆过的,没有明确的方法名,这样去找加解密函数无疑是大海捞针

image-20250616142411689

这里有开源的工具,blutter 生成脚本,使用ida pro 加载脚本

1
python blutter.py /path/your/apk out_dir

image-20250618153037965

加载完脚本后,就可以看到有可读的函数名

image-20250616142617946

搜索SM4,可以发现sm_crypto相关的函数

image-20250616142651112

找到这个 encrypt_bac8ac函数

image-20250616143252879

这里就要使用到 刚才 blutter 执行后生成的 frida 脚本, 把要hook的地址和要获取的参数设置好后

image-20250616143221967

然后就要使用到 frida

1
frida -U -f com.xxx.xxx -l blutter_frida.js

这里简单介绍一下 frida 这几个参数

1
2
3
4
-U 是指用 usb 连接
-R 远程连接 模拟器应该是要用这个参数
-f 是要调试的应用
-l 加载要执行的脚本

然后最终的效果如下:

image-20250619162543547

这里成功hook到了请求参数,接下来就是要一步步结合伪代码和汇编去分析加解密逻辑以及密钥生成算法,最终编写一个python/java脚本,实现请求参数的加解密以及响应的解密。

这里很自然会引申到另一个思路,既然app应用不好去逆向加解密算法,可以找对应的web应用,然后分析两个加解密方案是否一致,如果一致就可以去浏览器中调试js,然后写加解密脚本,然后应用到app中。

参考:

  1. flutter初探
  2. 逆向利器:frida
  3. 【flutter对抗】blutter使用+ACTF习题
  4. 手把手教你逆向Flutter App
  5. 深入理解burp抓包
  6. flutter app抓包姿势