
1. 项目概述当APP安全机制成为“拦路虎”在移动应用安全测试或逆向分析的过程中我们常常会遇到一个令人头疼的局面当你兴致勃勃地打开抓包工具准备一窥APP的网络通信时却发现应用要么直接闪退要么弹出一个“网络环境不安全”的提示然后拒绝任何网络请求。这背后往往是应用开发者部署的“反代理”与“反证书检测”机制在起作用。这些机制就像给APP穿上了一层“防刺服”旨在防止中间人攻击保护数据传输安全但也给我们的安全评估、调试或研究设置了障碍。我最近就遇到了一个金融类APP它使用了多种手段来检测系统代理和证书状态常规的抓包方法完全失效。经过一番折腾我总结出了一套行之有效的免费方案成功绕过了这些防线。这篇文章就是这次“实战”的完整记录。无论你是安全研究人员、移动应用开发者想测试自己APP的防护强度还是对移动安全感兴趣的学习者这套从原理到实操的“组合拳”都能为你提供清晰的路径。我们将彻底拆解APP如何检测代理与证书并一步步演示如何用免费工具“拆解”这些防护。2. 核心防线拆解APP如何感知“被窥探”在开始“绕过”之前我们必须先理解“防线”是如何建立的。知其然更要知其所以然这样才能针对性地找到破绽。2.1 反代理检测的常见手段APP检测代理主要是为了防止流量被第三方工具如Charles、Fiddler、Burp Suite截获和分析。其原理是检查系统是否设置了全局HTTP/HTTPS代理。2.1.1 系统属性检查针对Android这是最基础也是最常见的检测方式。APP会读取系统的http.proxyHost和http.proxyPort属性。如果这些属性不为空且指向一个非预期的地址如本地环回地址127.0.0.1或localhostAPP就会判定当前处于代理环境。// Java示例代码 String proxyHost System.getProperty(http.proxyHost); String proxyPort System.getProperty(http.proxyPort); if (proxyHost ! null !proxyHost.isEmpty()) { // 触发代理检测逻辑可能终止运行或清空网络栈 throw new RuntimeException(Proxy detected!); }实操心得很多早期或防护简单的APP仅依赖这一层检查。绕过方法相对简单可以通过Hook挂钩这些系统属性的获取方法使其始终返回null。2.1.2 网络连接信息检查更深入的检测会通过ConnectivityManager或Network类来获取当前活动网络的详细配置检查其是否使用了代理。// Android中检查WIFI代理的示例 ConnectivityManager cm (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo cm.getActiveNetworkInfo(); if (networkInfo ! null networkInfo.getType() ConnectivityManager.TYPE_WIFI) { ProxyInfo proxyInfo cm.getDefaultProxy(); if (proxyInfo ! null) { // 检测到代理配置 } }注意事项这种方法能检测到更具体的代理配置包括PAC代理自动配置脚本。单纯的系统属性修改可能无法绕过。2.1.3 原生层Native检测为了增加逆向难度一些安全要求高的APP会将检测逻辑写在C/C代码中编译进SO库。它们可能直接读取/proc/net/tcp等系统文件或调用底层的网络API来检查是否有本地端口被监听这正是抓包工具的工作方式。这种检测在Java层不可见静态分析和动态Hook的难度都更大。2.2 反证书检测SSL Pinning的深度解析反代理检测是“第一道门”而SSL证书绑定SSL Pinning则是更核心的“第二道锁”。它的目的是确保APP只与自己信任的特定服务器证书或公钥通信从而防止攻击者安装自定义根证书进行中间人攻击。2.2.1 证书绑定的实现层级网络框架层绑定在使用OkHttp、Retrofit等流行网络库时开发者可以很方便地配置证书绑定。例如OkHttp的CertificatePinner类允许开发者指定特定主机名必须匹配的证书指纹SHA-256或SHA-1。// OkHttp证书绑定示例 val certificatePinner CertificatePinner.Builder() .add(api.securebank.com, sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) .build() val client OkHttpClient.Builder() .certificatePinner(certificatePinner) .build()如果中间人证书的指纹不匹配连接会立即被终止。系统证书库信任链验证即使没有显式绑定APP默认也会信任系统预置的根证书库Android系统CA证书。当我们把Burp或Charles的根证书安装到系统“受信任的凭据”中时就相当于“骗过”了这一层验证。反证书检测会主动检查是否有多余的、可疑的特别是自签名的根证书被安装。自定义信任管理器TrustManager这是更灵活也更隐蔽的方式。APP可以完全绕过系统的证书验证流程自己实现X509TrustManager接口在checkServerTrusted方法中编写自定义的验证逻辑比如只认可自己硬编码在代码里的某几个特定证书。public class MyTrustManager implements X509TrustManager { Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { // 不是调用标准的验证流程而是与自己内置的证书对比 if (!isMyCertificate(chain[0])) { throw new CertificateException(Server certificate not trusted!); } } // ... 其他方法 }踩过的坑对付自定义的TrustManager常规的安装系统证书方法完全无效。必须修改APP的运行时代码要么Hook这个自定义验证方法使其总是通过要么替换掉整个SSL上下文。2.2.2 证书检测的主动扫描除了在通信时验证一些APP还会在启动或定期扫描系统的证书存储区寻找已知的抓包工具证书如PortSwigger CA、Charles Proxy CA。一旦发现就会提示风险并退出。这种检测独立于SSL握手过程需要单独处理。3. 免费工具链搭建与环境准备工欲善其事必先利其器。我们完全可以使用一套免费、开源的工具链来完成整个绕过过程。以下是我亲测可用的组合运行在Windows或macOS宿主机上通过虚拟化技术操作Android环境。3.1 核心工具选型与原理Android模拟器Android Studio 内置模拟器 (AVD)为什么选它相比真机模拟器具有更高的可控性和可重复性。我们可以轻松获取Root权限、修改系统镜像、快照回滚这对调试和测试至关重要。Genymotion等第三方模拟器虽然也不错但AVD免费、官方且与开发环境集成度最高。关键配置创建AVD时必须选择x86 或 x86_64 架构的系统镜像如Android 11.0 x86_64。因为后续使用的核心工具Frida在x86架构下的稳定性和兼容性最好。同时在“Advanced Settings”中将“Internal Storage”和“SD card”大小适当调大如2GB以便安装更多工具。动态插桩框架Frida核心作用Frida是一个动态代码插桩工具包。它允许我们将JavaScript代码片段注入到目标APP或系统进程的运行时内存中从而Hook拦截和修改函数调用、操作内存数据。它是绕过各种检测的“瑞士军刀”。工作原理Frida在目标端运行一个守护进程frida-server与桌面端的控制脚本通过管道通信。脚本中定义的Hook逻辑会被注入到目标进程实时改变其行为。获取从Frida官网 (frida.re) 下载对应版本的frida-server(Android x86_64) 和通过pip安装frida-tools。逆向分析辅助Objection为什么选它Objection是基于Frida的命令行工具封装了许多针对移动端尤其是Android/iOS安全的常用测试命令。它可以让SSL Pinning绕过、Root检测绕过等操作变得像输入一行命令那么简单极大提升了效率。获取通过pip安装pip install objection抓包工具Burp Suite Community Edition 或 mitmproxyBurp Suite功能强大的图形化抓包和渗透测试平台社区版免费。我们将用它来设置上游代理并安装其CA证书。mitmproxy轻量级、命令行驱动的中间人代理完全免费开源脚本化能力强。适合喜欢命令行操作和自动化集成的用户。二选一即可本文以Burp Suite为例进行演示。3.2 环境配置详细步骤3.2.1 模拟器与系统准备启动AVD在模拟器的“设置” - “关于平板电脑” - 多次点击“版本号”以开启“开发者选项”。进入“开发者选项”开启“USB调试”。这是Frida能够连接设备的基石。为了后续方便同时开启“网络ADB调试”允许通过网络连接ADB。3.2.2 推送Frida-server并启动从官网下载与本地frida-tools版本匹配的frida-server-x.x.x-android-x86_64.xz解压得到frida-server文件。使用ADB命令将文件推送到模拟器并赋予执行权限adb push frida-server /data/local/tmp/ adb shell chmod 755 /data/local/tmp/frida-server在模拟器的shell中以后台方式启动frida-serveradb shell cd /data/local/tmp ./frida-server 在宿主机终端验证连接frida-ps -U。如果能看到设备上的进程列表说明Frida环境搭建成功。3.2.3 配置抓包代理启动Burp Suite在Proxy - Options中确保代理监听在0.0.0.0:8080而不仅仅是127.0.0.1这样模拟器才能访问到。在模拟器的WIFI设置中长按已连接的网络 - 修改网络 - 高级选项 - 代理选择“手动”。代理主机名填写你宿主机的IP地址在macOS/Linux上用ifconfigWindows上用ipconfig查看通常是10.0.2.2或192.168.x.x。代理端口8080。此时模拟器的HTTP流量应该已经能流向Burp。访问一个HTTP网站测试。3.2.4 安装Burp的CA证书到系统信任区这是绕过基础SSL验证的关键一步但仅对未做SSL Pinning的APP有效。在模拟器浏览器中访问http://burp下载Burp的CA证书cacert.der。将证书文件后缀改为.cer。进入系统“设置” - “安全” - “加密与凭据” - “从存储设备安装证书” - “CA证书”找到并安装该证书。重要检查安装后在“受信任的凭据” - “用户”标签页下应能看到“PortSwigger CA”的证书。至此基础抓包环境就绪。4. 实战绕过从反代理到SSL Pinning假设我们的目标APP名为com.secure.app。完成上述准备后你会发现直接打开APP它可能检测到代理而无法联网或者即使能联网HTTPS请求也会因SSL Pinning而失败。下面我们分步攻克。4.1 阶段一禁用反代理检测逻辑我们使用Frida来Hook常见的代理检测方法。创建一个名为disable_proxy.js的脚本Java.perform(function() { console.log([*] Starting anti-proxy detection bypass...); // 1. Hook System.getProperty var System Java.use(java.lang.System); System.getProperty.overload(java.lang.String).implementation function(key) { if (key.includes(proxy)) { console.log([] System.getProperty called for key: key); // 对于代理相关的属性返回null return null; } return this.getProperty(key); }; // 2. Hook android.net.ProxyInfo (常见于检查当前网络代理) var ProxyInfo Java.use(android.net.ProxyInfo); if (ProxyInfo) { ProxyInfo.getHost.implementation function() { console.log([] ProxyInfo.getHost() called, returning null); return null; }; ProxyInfo.getPort.implementation function() { console.log([] ProxyInfo.getPort() called, returning -1); return -1; }; } // 3. Hook ConnectivityManager.getDefaultProxy (另一种常见方式) var ConnectivityManager Java.use(android.net.ConnectivityManager); if (ConnectivityManager) { ConnectivityManager.getDefaultProxy.implementation function() { console.log([] ConnectivityManager.getDefaultProxy() called, returning null); return null; }; } console.log([*] Anti-proxy hooks installed.); });使用Frida加载此脚本到目标APPfrida -U -f com.secure.app -l disable_proxy.js --no-pause-f参数表示启动APP--no-pause表示立即启动主线程。观察控制台输出如果看到Hook成功的日志并且APP不再因代理检测而崩溃或断网说明第一步成功。注意事项有些APP的检测逻辑可能在Native层或者使用了更冷门的API。如果上述脚本无效需要结合静态分析使用Jadx-GUI反编译APK搜索proxy、Proxy、getProxy等关键词找到具体的检测类和方法然后针对性地编写Hook脚本。4.2 阶段二绕过SSL证书绑定SSL Pinning这是攻坚的核心。我们使用Objection这个“神器”它能自动化完成许多常见的SSL Pinning绕过。4.2.1 使用Objection的自动化绕过首先确保目标APP进程正在运行如果上一步已用Frida启动则已运行。然后使用Objection连接并注入通用绕过脚本objection -g com.secure.app explore进入Objection的交互命令行后执行android sslpinning disable这条命令会尝试一系列操作寻找并Hook常见的证书验证类如TrustManagerImpl、OkHttp的CertificatePinner、Apache HttpClient的SSLSocketFactory等。将这些类的关键验证方法如checkServerTrusted、verify设置为空实现或修改其逻辑使其总是返回成功。尝试从内存中搜索并解除证书绑定。执行成功后Objection会输出类似“SSL Pinning disabled”的信息。此时再尝试在APP内触发网络请求Burp Suite中应该就能成功截获和解密HTTPS流量了。4.2.2 当Objection失效时的手动深度Hook有些应用使用了自定义的SSL验证库或者代码混淆严重Objection的通用脚本可能无法识别。这时就需要我们手动分析并Hook。步骤一定位关键验证点使用Jadx-GUI打开目标APK。搜索关键词X509TrustManager、checkServerTrusted、CertificatePinner、SSLContext、TrustManager。仔细查看搜索结果找到自定义的TrustManager实现类或证书绑定代码。记下完整的类名和方法名。步骤二编写精准Hook脚本假设我们找到了一个自定义类com.secure.app.network.CustomTrustManager它重写了checkServerTrusted方法。我们可以编写如下Frida脚本 (bypass_custom_pinning.js)Java.perform(function() { console.log([*] Attempting to bypass custom SSL pinning...); var CustomTrustManager Java.use(com.secure.app.network.CustomTrustManager); if (CustomTrustManager) { console.log([] Found CustomTrustManager class.); CustomTrustManager.checkServerTrusted.implementation function(chain, authType) { console.log([] CustomTrustManager.checkServerTrusted() HOOKED!); console.log( |- Auth type: authType); // 直接什么都不做让验证通过 // 如果需要也可以在这里打印或操作证书链信息 // var cert chain[0]; // console.log( |- Subject: cert.getSubjectDN().getName()); return; // 不抛出异常即表示验证通过 }; console.log([*] CustomTrustManager hook installed.); } // 同时也Hook标准的TrustManagerFactory以防万一 var TrustManagerFactory Java.use(javax.net.ssl.TrustManagerFactory); TrustManagerFactory.getTrustManagers.implementation function() { console.log([] TrustManagerFactory.getTrustManagers() called.); var originalManagers this.getTrustManagers(); // 这里可以替换或修改返回的TrustManager数组 return originalManagers; }; });使用Frida加载这个脚本frida -U -F com.secure.app -l bypass_custom_pinning.js-F参数表示附加到最前台的APP。实操心得手动Hook的关键在于精准定位。有时类名可能被混淆如变成a.a.a.c这时需要结合代码上下文如方法参数、调用的其他API和行为分析来判断。可以先用Objection尝试如果失败再根据Objection的日志或错误信息来缩小搜索范围。4.3 阶段三对抗证书存储区扫描如果APP在启动时扫描系统证书库并发现了Burp证书我们有两个策略临时重命名/隐藏证书推荐在安装Burp证书时不要使用默认名称。安装后通过ADB shell进入/data/misc/user/0/cacerts-added/(路径可能因系统版本而异)找到对应的证书文件一个数字哈希名.0将其临时移动到其他目录或改名。需要抓包时再移回来。这需要Root权限。adb shell su mv /data/misc/user/0/cacerts-added/xxxxxx.0 /data/local/tmp/ # 需要抓包时 mv /data/local/tmp/xxxxxx.0 /data/misc/user/0/cacerts-added/Hook证书扫描逻辑分析APP扫描证书的代码可能涉及KeyStore、CertificateFactory等类Hook相关方法在返回的证书列表中过滤掉Burp的证书。这需要对代码有更深的理解。5. 疑难排查与进阶技巧即使按照上述步骤操作你也可能会遇到各种问题。这里记录一些常见的“坑”和解决方法。5.1 常见问题速查表问题现象可能原因排查步骤与解决方案Frida连接失败frida-ps -U无输出1.frida-server未运行或崩溃。2. 设备USB调试未开启。3. 端口冲突或被防火墙阻止。1. 检查adb shell中frida-server进程是否存在 (ps | grep frida)。2. 重启frida-server。3. 确认开发者选项和USB调试已开启。4. 尝试使用frida-ps -U时指定传输方式-D 000000000000(设备ID)。Objection执行ssl pinning disable后仍抓不到包1. APP使用了Objection不支持的Pinning方案如自定义Native验证。2. 反代理检测仍然生效导致无网络流量。3. 脚本注入的时机不对验证逻辑在注入前已执行。1. 使用android hooking list classes搜索ssl、cert等关键词手动分析并Hook。2. 确认反代理Hook脚本已生效查看日志。3. 尝试在APP启动早期就注入脚本 (-f参数启动)。4. 使用-f并加上--pause在脚本加载后再恢复APP运行 (%resume)。APP启动立即闪退1. APP有强大的反调试或反注入机制。2. Frida或Hook脚本被检测。3. Hook脚本本身有错误导致目标进程崩溃。1. 尝试使用Frida的隐身模式重命名frida-server为其他名字如fs1286并相应修改连接代码。2. 寻找并Hook反调试检测点如android.os.Debug.isDebuggerConnected()。3. 分步注释Hook脚本定位导致崩溃的Hook点。4. 使用frida -U -f com.xxx --no-pause但不加载脚本看是否仍闪退以判断是Frida本身被检测。Burp能抓到HTTP包但HTTPS包显示Client TLS handshake failed1. 系统CA证书未正确安装或不被信任。2. APP使用了证书绑定且绕过未成功。3. Android 7.0 上APP可能设置了网络安全配置只信任系统预装证书。1. 确认Burp CA证书已安装在“系统信任的凭据”中用户目录。2. 重点检查并执行SSL Pinning绕过步骤。3. 对于Android 7需将Burp证书安装到系统分区需Root或修改APP的网络安全配置文件需反编译重打包。流量时有时无或特定请求失败1. APP对不同域名或API使用了不同的网络库或配置。2. 证书绑定可能只针对核心API域名其他静态资源域名未绑定。1. 在Burp中观察是哪些Host的请求失败。针对特定域名进行更深入的代码分析。2. 确认Hook脚本是否覆盖了所有可能的网络请求路径如WebView、图片库等发起的请求。5.2 进阶技巧与深度防御对抗5.2.1 应对Native层检测如果APP的核心检测逻辑在SO库中就需要使用Frida的Native API (Interceptor) 进行Hook。// 示例Hook Native层的 getaddrinfo 函数可能用于检测本地代理 Interceptor.attach(Module.findExportByName(null, getaddrinfo), { onEnter: function(args) { var hostname Memory.readCString(args[0]); console.log([Native] getaddrinfo called for: hostname); // 可以在这里修改hostname参数例如将指向代理检测服务器的域名解析到一个无效地址 }, onLeave: function(retval) { // 可以修改返回值 } });这需要对Linux系统API和ARM/X86汇编有一定了解。5.2.2 使用r0capture进行全能抓包如果觉得配置Frida脚本太麻烦可以尝试一个强大的集成工具r0capture。它是一个基于Frida的抓包工具专门针对安卓应用可以自动绕过SSL Pinning和代理检测。# 安装 pip install frida-tools git clone https://github.com/r0ysue/r0capture # 使用 (需先运行frida-server) python r0capture.py -U -f com.secure.app -v它会自动注入一系列绕过脚本并将解密后的HTTP/HTTPS流量以pcap格式保存方便用Wireshark分析。注意这是一个中国安全研究员开发的开源工具使用前请确保符合你的测试规范。5.2.3 模拟器指纹修改一些高级APP会检测运行环境是否为模拟器通过检查IMEI、Build属性、传感器等。如果检测到是模拟器可能会拒绝运行或启用更严格的防护。我们可以使用Magisk模块如MagiskHide Props Config或专门修改模拟器指纹的工具来伪装成真实设备。5.2.4 对抗代码混淆与加固对于经过严重混淆或商业加固如梆梆、爱加密的APP直接反编译可能看不到有意义的Java代码。这时动态分析优先直接运行APP用Frida去Hook那些在运行时必然会调用的系统级API如SSLContext.init、TrustManagerFactory.getInstance从调用栈回溯来定位关键代码区域。脱壳对于加固APP可能需要先进行脱壳获取原始的DEX文件。这本身就是一个复杂的话题涉及动态加载、内存Dump等技术有特定的工具链如Frida-DEXDump、FART。耐心与经验分析这类APP更像是一场耐心的较量。结合静态分析看汇编或经过简单反混淆的代码、动态调试、网络行为监控一点点拼凑出它的防护逻辑。绕过APP的安全防线是一个“道高一尺魔高一丈”的持续对抗过程。我个人的体会是没有一劳永逸的银弹。最有效的方法是建立一套清晰的排查思路先确保基础代理和证书环境无误然后用Objection等自动化工具尝试通用绕过失败则静下心来逆向分析从网络行为、日志、崩溃点中寻找线索最后编写针对性的Hook脚本。整个过程不仅考验技术更考验耐心和系统性思维。最后记住所有这些技术都应在合法授权的范围内进行用于安全研究、自我测试或已获许可的评估这是每一位从业者必须坚守的底线。