1. 项目概述一个为Android应用注入代理能力的开源工具如果你是一名移动应用开发者、安全研究员或者是对网络流量分析感兴趣的爱好者那么你很可能遇到过这样的场景你需要观察一个Android应用在运行时究竟发出了哪些网络请求请求里包含了什么参数服务器又返回了什么数据。无论是为了调试API接口、分析应用行为还是进行安全审计抓包都是最直接的手段。然而在Android生态里特别是面对那些使用了证书绑定SSL Pinning或对代理检测非常敏感的应用时传统的抓包方法比如在系统Wi-Fi设置里配置一个HTTP代理往往会失效。应用要么直接网络错误要么干脆检测到代理后拒绝连接。这就是anand-92/droidproxy这个项目诞生的背景。它是一个开源的、基于Frida的动态代码注入工具专门设计用来在Android应用运行时强制为其网络流量设置一个HTTP/HTTPS代理。简单来说它不依赖于修改系统设置或应用本身而是通过“注入”代码在应用进程内部“说服”它嘿你应该把所有网络请求都发到那个指定的代理服务器去。我最初接触这个工具是在分析一个金融类App的通信协议时常规抓包工具全部失灵在社区里翻找解决方案时发现了它。实测下来它对于绕过那些基础的代理检测和证书绑定非常有效成为了我移动端逆向分析工具箱里的一个常备利器。它的核心价值在于“动态”和“非侵入式”。你不需要对目标APK进行反编译、重打包和签名这避免了很多麻烦也降低了对应用稳定性的影响。整个过程就像给运行中的应用打了一针“临时疫苗”让它暂时按照我们的规则来走网络流量重启应用后一切恢复原样。接下来我会详细拆解它的工作原理、具体怎么用以及在实际操作中会遇到哪些坑怎么填平这些坑。2. 核心原理与架构拆解它究竟是如何工作的要理解DroidProxy我们需要先理解Android应用网络请求的基本路径和常见的“防御”手段。2.1 Android网络栈与代理检测点一个Android应用发起网络请求最终都会通过底层的网络库无论是标准的java.net.HttpURLConnection、OkHttp还是Apache HttpClient。这些库在建立连接前通常会去检查系统的代理设置。这个检查的入口对于很多库来说是ProxySelector类。应用可以通过调用ProxySelector.getDefault().select(...)来获取当前系统为某个URI配置的代理。如果一个应用不想走代理它完全可以忽略这个默认选择器直接使用Proxy.NO_PROXY或者实现自己的代理选择逻辑。更“顽固”的应用会主动进行代理检测。它们可能会检查系统属性如读取http.proxyHost和http.proxyPort。检查ConnectivityManager获取网络信息判断是否设置了代理。证书绑定SSL Pinning这是HTTPS抓包的最大拦路虎。应用在代码中硬编码了它信任的服务器证书或公钥哈希。当建立TLS连接时它会对比服务器传来的证书和它内置的“白名单”如果不匹配即使你在设备上安装了抓包工具如Charles或Fiddler的根证书连接也会被拒绝。2.2 DroidProxy的“注入”策略DroidProxy没有去修改系统环境那需要root权限而是选择了一个更精巧的层面应用运行时内存。它借助了Frida这个强大的动态插桩框架。Frida允许我们在目标应用进程运行时注入一段JavaScript或Python代码这段代码可以拦截和替换应用原有的函数调用。DroidProxy的核心脚本做的就是以下几件事挂钩Hook关键函数它主要挂钩了java.net.ProxySelector类的select方法。当应用调用这个方法去获取代理时DroidProxy的代码会抢先一步执行并直接返回一个我们预设好的代理地址例如127.0.0.1:8080而不是系统真实的代理设置。这就“欺骗”了应用让它以为系统指定的代理就是我们想要的抓包代理。覆盖代理相关属性同时它也会挂钩System.getProperty等方法。当应用尝试读取http.proxyHost这类属性时同样返回我们注入的值。绕过证书绑定部分对于某些使用特定库如OkHttp的证书绑定DroidProxy的脚本可能包含了对CertificatePinner等类的挂钩使其验证逻辑失效。但需要注意的是证书绑定的实现方式多种多样DroidProxy并非万能钥匙对于深度自定义的绑定可能无效这时需要配合其他Frida脚本或Xposed模块。架构流程可以简化为你的电脑运行Frida Server DroidProxy脚本 - USB调试/网络 - Android设备运行目标App | 注入JavaScript代码 | Hook ProxySelector.select() 等方法 | 强制返回指定代理:127.0.0.1:8080 | 目标App的网络流量 - 你的抓包工具(如Charles)注意这个过程完全发生在目标应用进程的内存空间里不会修改任何应用文件或系统文件。一旦应用进程结束注入的代码也就消失了。2.3 与同类工具的对比在DroidProxy出现之前常见的方案有Xposed模块如JustTrustMe功能强大且系统级生效。但它需要安装Xposed框架对系统侵入性大且在新版本Android上安装麻烦兼容性问题多。修改APK反编译后修改Smali代码或直接修改网络库然后重打包签名。过程繁琐且可能触发应用自身的签名校验导致崩溃。iptables透明代理需要root权限配置复杂。DroidProxy的优势在于轻量、动态、无需root仅需ADB调试权限。它的劣势是每次都需要手动附加到进程并且对于某些极其严格的应用可能仍然无效。它更像一把精准的“手术刀”而不是“重锤”。3. 环境准备与详细实操步骤理论讲完了我们上手操作。整个过程可以分解为四个阶段环境搭建、工具准备、脚本执行、流量捕获。3.1 阶段一基础环境搭建你需要准备两台“机器”一台是你的分析主机通常是Windows、macOS或Linux的电脑另一台是目标Android设备手机或模拟器。1. 在分析主机上安装Python 3.x这是运行Frida客户端和DroidProxy脚本的基础。去官网下载安装并确保python和pip命令可用。Frida客户端通过pip安装。打开命令行终端执行pip install frida-tools这个命令会同时安装frida和frida-tools。安装完成后可以用frida --version检查。ADBAndroid Debug Bridge这是与Android设备通信的桥梁。如果你有Android Studio它自带ADB。也可以单独下载SDK Platform-Tools。安装后将ADB所在目录加入系统PATH环境变量。抓包工具推荐Charles或Fiddler。以Charles为例你需要知道它的代理监听端口默认8888。确保Charles已经启动并开启了代理功能。2. 在目标Android设备上安装开启开发者选项与USB调试这是必须的。在设备的“设置”-“关于手机”里连续点击“版本号”7次开启开发者选项。然后在开发者选项中打开“USB调试”。安装Frida Server这是Frida在设备端运行的守护进程。首先用adb shell进入设备shell执行getprop ro.product.cpu.abi查看设备架构通常是arm64或x86_64。去Frida的GitHub Releases页面下载对应架构的Frida Server文件如frida-server-16.1.4-android-arm64.xz。解压得到frida-server文件通过adb push将其推送到设备例如adb push frida-server /data/local/tmp/。通过adb shell进入设备切换到文件所在目录赋予可执行权限并运行adb shell su # 如果设备已root获取root权限能避免后续权限问题 cd /data/local/tmp chmod 755 frida-server ./frida-server 保持这个shell窗口不要关闭或者让Frida Server在后台运行。你可以另开一个终端窗口进行后续操作。3.2 阶段二获取与理解DroidProxy脚本DroidProxy是一个GitHub仓库我们需要获取它的核心JavaScript脚本。git clone https://github.com/anand-92/droidproxy.git cd droidproxy核心文件通常是droidproxy.js或类似名称。用文本编辑器打开它你可以看到前面原理部分提到的那些Hook点。理解脚本内容有助于你在它失效时进行调试或修改。脚本开头部分通常会有配置项比如var proxyHost 127.0.0.1; var proxyPort 8080;这里就是设置你要将流量重定向到的代理地址。如果你的抓包工具如Charles运行在电脑上并通过Wi-Fi代理到手机那么这里应该填你电脑在手机所在Wi-Fi网络中的IP地址端口填Charles的监听端口如8888。如果使用模拟器127.0.0.1通常就指向电脑本地。3.3 阶段三执行注入与捕获流量这是最关键的一步。假设我们要分析一个包名为com.example.targetapp的应用。确保设备连接并找到进程在分析主机的终端里执行adb devices确保设备已列出。然后启动目标应用。附加进程并注入脚本在droidproxy目录下执行以下命令frida -U -f com.example.targetapp -l droidproxy.js --no-pause-U: 连接到USB设备。-f com.example.targetapp: 以-f参数启动应用。如果应用已经在运行你可以先用frida-ps -U找到进程PID然后用-p PID来附加。-l droidproxy.js: 加载我们的脚本。--no-pause: 启动后立即恢复进程运行如果不加进程会暂停直到你在Frida CLI里输入%resume。观察与验证如果一切顺利Frida会附加成功并在终端输出一些Hook成功的日志比如“ProxySelector hooked!”。此时目标应用的所有通过被Hook渠道的网络请求都应该被转发到你设置的代理。在抓包工具中查看流量打开Charles你应该能看到来自目标设备的HTTP/HTTPS请求。对于HTTPS请求你需要在设备上安装Charles的根证书通过访问chls.pro/ssl并且可能在DroidProxy脚本生效后应用才会信任这个证书。实操心得第一次运行时最容易出错的地方是代理地址配置不对。如果Charles里看不到任何来自目标App的流量首先检查手机Wi-Fi代理是否已经设置到了Charles对于DroidProxy这一步有时不是必须的但作为备份检查点。droidproxy.js脚本里的proxyHost和proxyPort是否指向了正确的抓包工具地址和端口尝试在目标App内手动触发一个网络操作如下拉刷新看看Charles是否有请求进来。查看Frida终端的输出是否有错误信息。3.4 针对HTTPS流量的特殊处理仅仅设置代理对于HTTPS来说还不够因为会涉及SSL中间人攻击的证书问题。在设备上安装抓包工具的根证书这是必须的。让手机和抓包工具Charles处于同一Wi-Fi在手机浏览器访问http://charlesproxy.com/getssl或chls.pro/ssl下载并安装证书。在Android 7.0及以上版本为了让用户安装的证书被应用到所有应用特别是目标App你必须将证书安装到“系统级”证书存储区而这通常需要root权限。如果没有root一个变通方法是将抓包工具的证书文件.pem或.der格式推送到设备然后在Frida脚本中动态修改目标App的证书信任库这是一个更高级的操作DroidProxy的基础脚本可能不包含。DroidProxy与证书绑定的对抗很多应用会使用证书绑定。DroidProxy的脚本可能包含了对OkHttp CertificatePinner、TrustManager等类的Hook来禁用绑定。你可以在脚本中搜索CertificatePinner、check、TrustManager等关键词来确认。如果脚本没有生效你可能需要寻找或自己编写更针对性的Frida脚本来Hook具体的绑定验证方法。4. 高级用法与定制化脚本基础用法能解决大部分中等强度的代理检测。但对于“硬骨头”我们需要更深入。4.1 脚本调试与修改Frida提供了强大的交互式控制台。在注入脚本时你可以不加--no-pause这样应用会暂停你进入Frida的REPL交互式环境可以手动执行JavaScript代码来测试Hook点。例如你可以手动调用Java.perform来挂钩一个函数看看是否成功// 在Frida CLI里输入 Java.perform(function() { var ProxySelector Java.use(java.net.ProxySelector); ProxySelector.select.implementation function(uri) { console.log([*] ProxySelector.select called for: uri); // 返回自定义代理逻辑 var myProxy Java.use(java.net.Proxy).$new(Java.use(java.net.Proxy).Type.HTTP, Java.use(java.net.InetSocketAddress).$new(127.0.0.1, 8888)); return Java.array(java.net.Proxy, [myProxy]); }; });如果这个手动Hook能成功抓到流量但DroidProxy脚本不行说明脚本的Hook点可能不对或者目标App使用了更冷门的网络库。4.2 扩展Hook点以应对更多网络库DroidProxy的默认脚本可能主要针对标准Java网络库。但现在很多App使用OkHttp或Retrofit。你需要扩展Hook点。Hook OkHttp的OkHttpClient.BuilderOkHttpClient在构建时可以通过.proxy()方法设置代理。我们可以Hook这个方法的调用。Java.perform(function() { var Builder Java.use(okhttp3.OkHttpClient$Builder); Builder.proxy.implementation function(proxy) { console.log([*] OkHttpClient.Builder.proxy() called, overriding...); // 强制返回我们设定的代理 var newProxy Java.use(java.net.Proxy).$new(Java.use(java.net.Proxy).Type.HTTP, Java.use(java.net.InetSocketAddress).$new(proxyHost, proxyPort)); return this.proxy(newProxy); }; });Hook 系统属性读取有些应用会直接读系统属性。Java.perform(function() { var System Java.use(java.lang.System); System.getProperty.overload(java.lang.String).implementation function(key) { if (key http.proxyHost) { return proxyHost; } else if (key http.proxyPort) { return proxyPort.toString(); } return this.getProperty(key); }; });4.3 与其他工具联用DroidProxy可以成为更大分析工作流的一部分与r0capture联用r0capture是另一个强大的Frida脚本用于抓取所有Java层和Native层的TCP/UDP流量甚至能解密HTTPS。你可以同时注入DroidProxy和r0capture一个负责强制走代理以便用Charles可视化查看另一个负责全面记录和解密。与Objection联用Objection是一个基于Frida的运行时移动安全测试工具。你可以在Objection的环境中加载DroidProxy脚本并结合Objection的其他命令如android sslpinning disable来综合解决问题。5. 常见问题、排查技巧与实战心得在实际使用中你肯定会遇到各种问题。下面是我踩过的一些坑和解决方案。5.1 问题排查清单问题现象可能原因排查步骤与解决方案Frida连接失败提示Device not found或Unable to connect1. USB调试未开启。2. 设备未授权电脑。3. Frida Server未在设备上运行。4. 设备离线adb disconnect。1. 确认开发者选项和USB调试已开。2. 检查设备屏幕是否有“允许USB调试”弹窗点击允许。3.adb shell进入设备ps | grep frida查看进程重启frida-server。4. 执行adb devices重新连接。注入成功但Charles无流量1. 代理IP/端口配置错误。2. 目标App的网络请求未经过被Hook的函数。3. 抓包工具未正确监听。4. 应用有额外的代理检测并主动拒绝。1. 核对droidproxy.js中的proxyHost和proxyPort确保是Charles的地址。用手机浏览器访问http://proxyHost:proxyPort看能否看到Charles的欢迎页。2. 尝试在Frida中Hook更底层的Socket连接函数或使用r0capture等全流量捕获脚本验证App是否真有外联。3. 检查Charles的Proxy Settings确保监听端口正确且已启用。4. 分析App看是否有检测代理并切换为直连的代码需Hook其检测逻辑。HTTPS流量显示为Unknown或证书错误1. 设备未安装Charles根证书。2. 证书安装位置不对用户证书 vs 系统证书。3. App使用了证书绑定。1. 确保已通过chls.pro/ssl安装证书。2. Android 7需将证书移至系统证书目录需root或使用Frida脚本绕过证书验证。3. 使用objection的android sslpinning disable命令或寻找/编写针对该App证书绑定逻辑的Frida脚本。注入后App闪退或功能异常1. Frida脚本Hook了不稳定的函数导致崩溃。2. App有反调试/反Frida检测。1. 尝试注释掉脚本中部分Hook点定位导致崩溃的Hook。2. 使用反反调试技巧如Frida的--disable-anti参数或使用定制化的Frida Server隐藏特征。脚本输出“Unable to find class”错误目标App未使用该Java类或类名因混淆而改变。1. 使用frida-trace先跟踪网络相关API的调用确定实际使用的类和方法名。2. 对App进行初步逆向查看其网络库的使用情况。5.2 实战心得与技巧先静态分析再动态注入不要一上来就怼Frida。先用jadx-gui等工具反编译APK搜索Proxy、ProxySelector、OkHttpClient、CertificatePinner等关键词了解目标App使用了哪些网络库是否有明显的代理检测和证书绑定代码。这能让你有的放矢地修改或选择Hook脚本。从宽到窄进行Hook如果DroidProxy默认脚本无效可以先尝试用r0capture这种全量抓包脚本确认App有网络流量且能抓到。如果能抓到但走不了代理说明代理设置没生效。然后再逐步细化去Hook具体的代理设置点。模拟器是好朋友在真机上反复测试安装证书、重启、调试可能很麻烦。使用Android模拟器如Android Studio自带的或Genymotion可以快速快照和恢复状态大大提高效率。注意模拟器的网络桥接模式确保主机和模拟器能互通。保持工具链版本兼容Frida Server的版本和Frida ClientPython包的版本需要匹配。不匹配可能会导致连接失败或功能异常。尽量使用相同版本。理解局限DroidProxy本质是用户态的Hook。如果App将关键逻辑放在NativeC/C层或者使用了VPN、自定义Socket等非常规网络通道纯Java层的Hook可能无效。这时需要结合Native层的Hook工具如Frida的Interceptor进行分析。这个工具打开了移动应用网络分析的一扇窗但它不是万能的。它要求使用者对Android网络编程、Frida框架有一定的理解。当你成功地将一个“顽固”应用的流量导入抓包工具清晰地看到每一个请求和响应时那种成就感会让你觉得前期的折腾都是值得的。移动安全和分析就是一个不断与防护措施“斗智斗勇”的过程而DroidProxy无疑是一把趁手的好兵器。