Android 14/15 HTTPS抓包实战:系统证书注入与Frida绕过证书固定

1. 项目概述:为什么在Android 14/15上抓包变得如此棘手?

如果你最近尝试在最新的Android 14或15设备上抓取HTTPS流量,大概率会碰一鼻子灰。传统的“用户证书”安装法,在Chrome和很多主流App上已经彻底失效,你会看到“网络错误”或“证书不受信任”的提示。这不是你的操作有问题,而是Google从Android 7.0开始引入、并在后续版本中不断强化的网络安全策略——网络安全性配置(Network Security Configuration)和证书固定(Certificate Pinning)——在Android 14/15上达到了一个新的高度。简单来说,系统对非系统级根证书的信任度降到了冰点,尤其是对于以targetSdkVersion为目标的新应用。

这个项目的核心,就是攻克这两座大山:系统证书注入应用进程级捕获。前者解决的是“让设备信任你的抓包工具(如Charles、Fiddler)”的问题,后者解决的是“如何绕过应用自身的证书锁定和代码混淆,看到明文流量”的问题。这不再是一个简单的“设置代理、安装证书”就能搞定的事情,它涉及到对Android系统安全模型的理解、ADB调试权限的深度利用、以及一些“非常规”但有效的实战技巧。我花了相当长时间,在各种品牌和型号的Android 14/15设备上反复测试,总结出了一套稳定、可复现的完整流程,其中就包括如何利用一些社区工具(如sh /storage/emulated/0/android/data/com.omarea.vtools/up.sh这类脚本的变体思路)来简化系统级操作。

2. 核心思路与前置准备:理解Android的证书信任链

在动手之前,我们必须搞清楚Android是如何决定信任一个证书的。这直接决定了我们的操作路径。

2.1 Android的证书存储与信任机制

Android维护着两套主要的证书存储:

  1. 用户证书存储:路径通常为/data/misc/user/0/cacerts-added。用户手动安装的CA证书(如通过下载.cer文件安装)就放在这里。在Android 6.0及以前,应用默认会信任这里的证书。但从Android 7.0开始,应用可以通过声明Network Security Configuration文件,选择不信任用户证书。到了Android 14/15,越来越多的系统组件和预装应用(包括WebView)默认就不信任用户证书了。
  2. 系统证书存储:路径为/system/etc/security/cacerts/。这里的证书拥有最高的信任级别,所有应用(除非使用了证书固定)都必须信任。我们的终极目标,就是把抓包工具的CA证书放到这个目录下,并赋予正确的权限。

2.2 关键前提:获取系统级权限

/system分区写入文件,需要极高的权限。通常有两种途径:

  1. Root权限:最直接,但会触发设备保修失效、安全支付失效、且过程复杂风险高,不适用于所有设备(特别是国行旗舰机)。
  2. ADB Root(系统调试桥Root):这是本次实战的核心依赖。它要求你的设备系统镜像(通常是自定义ROM或开发者预览版)在编译时启用了ro.debuggable=1ro.adb.secure=0等属性,使得通过adb shell获得的shell用户(通常是shellroot)本身就拥有挂载系统分区为可写(rw)的权限。很多Pixel设备刷入原生Android开发者预览版(DP)或一些开源AOSP构建版本后,就具备这个条件。这也是为什么社区里流传的很多一键脚本(包括原理类似up.sh的脚本)都明确要求设备已开启“ADB Root”。

准备工作清单:

  • 一台已开启“开发者选项”和“USB调试”的Android 14/15设备。强烈建议使用Pixel系列刷入最新Android DP版本,或使用官方Android模拟器(其系统镜像默认具备ADB Root)。
  • 一台电脑,安装好对应设备的USB驱动、Android SDK Platform-Tools(包含adb命令)。
  • 抓包工具:Charles、Fiddler、mitmproxy任选其一。本文以Charles为例,因其界面友好,原理通用。
  • 基础命令行操作知识

3. 系统证书注入实战:将CA证书写入/system分区

这是最关键的步骤,一旦成功,设备上绝大多数HTTPS流量在抓包工具看来都将“畅通无阻”。

3.1 第一步:从抓包工具导出根证书

首先,确保电脑和手机在同一个局域网(Wi-Fi),并在抓包工具中设置好代理(如Charles默认的0.0.0.0:8888)。

  1. 在Charles中,点击Help -> SSL Proxying -> Save Charles Root Certificate...
  2. 选择保存类型为.cer文件(二进制的DER格式)。记住保存路径,比如charles_root.cer

    注意:不要保存为.pem(Base64编码)格式,虽然可以转换,但直接使用.cer更简单。

3.2 第二步:转换证书格式与计算哈希

Android系统证书存储要求证书文件以特定的哈希值命名。我们需要将.cer文件转换为.pem格式,并计算其哈希。

  1. 使用OpenSSL命令(电脑上需安装OpenSSL,或使用Git Bash等自带环境):
    # 将 .cer (DER) 转换为 .pem openssl x509 -inform DER -in charles_root.cer -out charles_root.pem # 计算 .pem 证书的哈希值(Subject Hash) openssl x509 -inform PEM -subject_hash_old -in charles_root.pem | head -1
    这条命令会输出一个8位的十六进制字符串,例如a0b1c2d3。这就是你的证书在系统存储中的文件名(不含扩展名)。

3.3 第三步:推送证书并挂载系统分区(ADB Root环境)

这里就是体现“系统级”操作的地方。假设上一步得到的哈希是a0b1c2d3

  1. 连接设备并获取ADB Root Shell

    adb devices # 确认设备已连接 adb shell # 进入设备的shell环境

    进入后,命令行提示符应该是$#。如果是#,说明已经是root。如果是$,尝试输入su,如果成功切换为#,则说明有ADB Root。

  2. 挂载系统分区为可读写/system分区默认是只读(ro)的。我们需要重新挂载它。

    # 查看/system分区挂载点,通常是 /dev/block/by-name/system 或类似,挂载在 /system mount | grep system # 输出可能类似:/dev/block/sda1 on /system type ext4 (ro,seclabel,relatime) # 重新挂载为可读写 mount -o rw,remount /system # 或者使用更明确的分区路径 # mount -o rw,remount /dev/block/sda1 /system

    执行成功后,/system分区就暂时可写了。

  3. 推送并放置证书文件: 保持adb shell#状态,在电脑的另一个命令行窗口执行:

    # 将.pem证书文件推送到设备的临时目录 adb push charles_root.pem /data/local/tmp/ # 回到adb shell窗口,执行以下命令 cd /data/local/tmp # 将证书复制到系统证书目录,并以哈希值命名,后缀为.0 cp charles_root.pem /system/etc/security/cacerts/a0b1c2d3.0 # 至关重要:设置正确的文件权限和所有者 chmod 644 /system/etc/security/cacerts/a0b1c2d3.0 chown root:root /system/etc/security/cacerts/a0b1c2d3.0

    实操心得:权限644(所有者可读写,其他人只读)和所有者root:root是系统证书的标准配置,必须严格设置,否则系统可能忽略该证书。

  4. 恢复系统分区只读属性并重启

    # 将/system分区改回只读,这是一个好习惯,避免误操作破坏系统 mount -o ro,remount /system # 或者 mount -o ro,remount /dev/block/sda1 /system # 重启设备,让系统重新加载证书存储 reboot

    设备重启后,你的Charles根证书就已经成为系统信任的根证书了。你可以在手机的设置 -> 安全 -> 更多安全设置 -> 加密与凭据 -> 信任的凭据 -> 系统中,找到以“Charles Proxy”或你自定义名称显示的证书。

3.4 关于“一键脚本”的解读

网络上流传的类似sh /storage/emulated/0/android/data/com.omarea.vtools/up.sh的命令,其本质就是将上述第三步的复杂命令序列写成了一个Shell脚本。这个脚本通常会被放在设备的某个可访问路径(如SD卡),然后通过adb shell sh <脚本路径>来执行。脚本内部一般包含:

  1. 检查是否具有root权限。
  2. 将预先放在同目录或指定位置的证书文件(如cacert.pem)复制到/system/etc/security/cacerts/
  3. 计算哈希并重命名,设置权限。
  4. 可能包含挂载/system为可写的命令。

使用此类脚本的注意事项

  • 绝对不要运行来源不明的脚本。脚本中如果包含rm -rf /之类的命令,你的设备就变砖了。
  • 运行前,最好用文本编辑器打开脚本,检查其具体操作。
  • 确保脚本中的证书文件路径正确,且证书文件确实存在。
  • 它并不能绕过“ADB Root”这个根本前提。如果设备本身不具备ADB Root权限,脚本会在挂载/system那一步失败。

4. 应用进程级捕获:攻克证书固定与代码混淆

即使完成了系统证书注入,你仍然可能遇到一些“顽固”的应用,它们使用了证书固定(Certificate Pinning)。这意味着应用在代码里硬编码了只信任特定的证书(通常是它自己服务器的证书或特定的CA),完全忽略系统证书存储。对于这类应用,我们需要进行“进程级”的干预。

4.1 方法一:使用高版本Android的“用户证书”变通方案(针对部分App)

在Android 14上,Google为高级用户留了一个后门。你可以通过ADB命令,强制将用户证书以更高权限安装。

adb shell am start -a android.settings.MANAGE_CA_CERTIFICATES

这个命令会打开一个隐藏的“管理CA证书”界面(如果你的系统有)。更直接的方法是使用ADB安装:

# 将证书文件推送到设备 adb push charles_root.cer /data/local/tmp/ # 使用特权命令安装 adb shell su -c "cp /data/local/tmp/charles_root.cer /data/misc/user/0/cacerts-added/$(openssl x509 -inform DER -outform PEM -in /data/local/tmp/charles_root.cer | openssl x509 -hash -noout).0"

这种方法有时可以绕过一些只检查“用户证书”而非“系统证书”的较弱实现,但面对强证书固定依然无效。

4.2 方法二:使用Frida进行运行时Hook(推荐)

这是目前最强大、最通用的解决方案。Frida是一个动态代码插桩工具,可以注入JavaScript到目标应用的进程中,实时修改其内存和行为。我们可以用它来Hook住应用执行SSL/TLS验证的代码,使其无条件信任我们的证书或直接绕过验证。

准备工作

  1. 在电脑上安装Frida:pip install frida-tools
  2. 在Android设备上安装Frida-server。需要根据设备CPU架构(arm,arm64,x86_64)下载对应版本,并通过ADB推送到设备上运行。
    adb push frida-server-16.1.11-android-arm64 /data/local/tmp/frida-server adb shell "chmod 755 /data/local/tmp/frida-server" adb shell "/data/local/tmp/frida-server &"

使用现成的Frida脚本: 社区有很多优秀的脚本可以绕过SSL Pinning。例如,frida-ssl-unpinning这个仓库收集了针对不同框架(OkHttp, Android原生, Xamarin等)的脚本。

  1. 找到目标应用使用的网络库(可以通过反编译APK查看AndroidManifest.xml或库文件猜测,或直接尝试通用脚本)。
  2. 在电脑上运行Frida命令注入脚本:
    frida -U -f com.example.targetapp -l ssl_unpinning.js --no-pause
    -U表示连接USB设备,-f后面跟包名以启动应用,-l指定要加载的JavaScript脚本。

编写简单的通用Hook脚本: 如果你不想用现成的,一个非常暴力的通用方法是Hook住java.security.cert.Certificate的验证方法。创建一个bypass_ssl.js文件:

Java.perform(function() { var CertificateFactory = Java.use('java.security.cert.CertificateFactory'); var X509Certificate = Java.use('java.security.cert.X509Certificate'); var TrustManagerImpl = null; // 尝试Hook常见的TrustManager实现 try { TrustManagerImpl = Java.use('com.android.org.conscrypt.TrustManagerImpl'); TrustManagerImpl.checkServerTrusted.implementation = function(chain, authType, host) { console.log("[+] Bypassing checkServerTrusted for: " + host); return; // 直接返回,不抛异常即表示信任 }; } catch(e) { console.log("[-] TrustManagerImpl not found: " + e); } // Hook X509Certificate的检查方法 X509Certificate.checkValidity.implementation = function() { console.log("[+] Bypassing certificate validity check"); return; }; });

运行frida -U -f com.example.app -l bypass_ssl.js,然后操作应用,观察Frida控制台输出和抓包工具,很多情况下流量就出来了。

踩坑实录:Frida-server的版本必须与电脑端frida-tools的版本兼容。最好使用相同的主要版本号。此外,一些应用(如银行、支付类)会有反调试、反Frida的机制,可能需要更复杂的对抗手段,如隐藏Frida特征、使用ptrace附加等,这属于更高级的范畴。

4.3 方法三:使用第三方修改版应用或核心破解(Magisk模块)

这是一种“釜底抽薪”的方法,但需要设备已Root(不仅仅是ADB Root)。

  1. 核心破解(Core Patch)Magisk模块:这是一个非常流行的模块,它可以在系统层面全局禁用证书固定和签名验证。安装后,绝大多数应用的证书固定都会失效。这对于不想折腾Frida脚本的用户来说是最方便的选择。
  2. 修改版应用:在一些第三方应用市场或论坛,可能存在已经去除了证书固定代码的App修改版。使用这类版本自然可以抓包。但安全风险极高,你无法保证修改者没有加入恶意代码,仅推荐在测试专用机上使用。

5. 网络配置与抓包工具设置

证书问题解决后,基础的网络配置也不能出错。

  1. 配置设备代理:在手机已连接的Wi-Fi网络设置中,修改代理为手动,填入运行抓包工具的电脑的局域网IP地址和代理端口(如Charles的8888)。
  2. 配置Charles SSL代理:在Charles中,进入Proxy -> SSL Proxying Settings,添加一个*:*的条目(主机为*,端口为*),表示代理所有SSL连接。
  3. 安装Charles证书到设备(仅辅助):尽管我们已经注入了系统证书,但有时为了方便,仍可以在设备浏览器访问chls.pro/ssl来安装Charles的用户证书。这对于抓取一些不严格校验证书来源的流量(如某些内嵌WebView)可能有帮助。
  4. 针对特定应用的抓包:在Charles的Proxy -> Access Control Settings中,确保允许你的设备IP连接。你还可以在Proxy -> SSL Proxying Settings中更精确地添加需要抓包的具体域名和端口,而不是使用通配符。

6. 实战问题排查与进阶技巧

即使按照上述步骤操作,你可能还是会遇到问题。以下是一些常见坑点及解决方案。

6.1 流量依旧不通或证书错误

  • 检查代理配置:确认手机Wi-Fi代理的IP和端口绝对正确。电脑防火墙是否放行了8888端口?可以尝试在手机浏览器访问http://<电脑IP>:8888,应该能看到Charles的欢迎页。
  • 确认证书生效:重启设备后,再次adb shell进入,执行ls -l /system/etc/security/cacerts/ | grep <你的证书哈希>,确认文件存在且权限是-rw-r--r-- root root
  • 清除应用数据:有些应用会在首次启动时缓存证书链。在注入系统证书后,去手机设置里找到目标应用,强制停止清除其缓存和数据,然后重新启动应用。
  • 检查目标应用特性:使用adb shell dumpsys package <包名> | grep targetSdk查看应用的targetSdkVersion。如果大于等于28(Android 9),它默认就不信任用户证书,我们的系统证书注入法对它就是必需的。如果大于等于34(Android 14),其网络限制会更严格。

6.2 使用了WebView或网络库的特定配置

一些应用内嵌的WebView或使用了像OkHttp这样的网络库,它们可能有独立的证书信任管理器。

  • 对于自定义WebView:应用可能通过WebViewClientonReceivedSslError方法处理错误,或者配置了自己的WebViewAssetLoader。这需要更具体的Frida脚本来Hook。
  • 对于OkHttp:OkHttp的证书固定非常常见。使用针对OkHttp的Frida脚本(如HookCertificatePinner类)通常能解决问题。社区脚本frida-ssl-unpinning里通常包含okhttp3.pinning.js

6.3 应对高强度加固与反调试

金融、社交类大型App常使用梆梆、腾讯御安全等第三方加固,并具备反调试、反注入能力。

  • 过反调试:Frida默认特征明显。可以尝试使用-f参数启动应用后立即暂停,然后使用frida -U --attach <进程名>来附加,而不是启动时注入。或者使用修改了特征名的Frida-server。
  • 过内存检测:有些应用会定期检查关键内存地址是否被修改。这需要动态分析,找到检测点并用Frida绕过,属于高阶逆向工程。
  • 使用Xposed/EdXposed/LSPosed:如果设备已Root,可以安装LSPosed框架,并安装诸如“TrustMeAlready”、“SSLUnpinning”之类的模块。这些模块在系统层面工作,兼容性有时比Frida脚本更好,但需要设备有完整的Magisk环境。

6.4 抓包工具的选择与对比

  • Charles/Fiddler:图形化界面友好,功能强大,适合分析HTTP/HTTPS流量,查看请求/响应详情,重放和断点调试。是入门和日常开发的首选。
  • mitmproxy:命令行工具,功能强大且可脚本化,适合自动化测试和高级用户。它同样需要安装CA证书。
  • Wireshark:抓取底层网络包(TCP/IP层),无需代理设置,但无法直接解密HTTPS流量(除非有服务器的私钥)。它更适合分析协议问题、网络延迟,而不是应用层数据抓取。
  • tcpdump(Android):在Android设备上直接运行tcpdump可以抓取设备自身的网络包,然后导出到电脑用Wireshark分析。这需要设备有root权限,并且也无法解密HTTPS。

我个人在Android 14/15上的组合是:系统证书注入 + Charles(日常分析) + Frida(应对顽固应用)。这个组合覆盖了95%以上的场景。

最后,所有抓包行为请务必在你自己拥有完全控制权的设备测试环境中进行,并严格遵守相关法律法规和服务条款。抓包是强大的分析和调试工具,正确使用它能极大提升开发、测试和安全研究效率。