1. 为什么需要拦截网络请求在移动应用开发和安全测试中网络请求拦截是个高频需求。你可能遇到过这些场景调试API接口时想查看原始数据包、分析第三方应用的数据交互逻辑、检测敏感信息是否加密传输等。传统做法是在代码中埋点打印日志但这需要重新编译打包效率太低。Frida作为动态插桩工具可以直接在运行时修改应用行为。我去年做金融App安全审计时就用它成功捕获了未加密的身份证照片传输。相比静态分析动态拦截能看到真实数据流还能实时修改请求参数测试边界情况。2. 准备工作与环境搭建2.1 工具准备清单你需要准备这些工具已root的Android设备或模拟器推荐GenymotionFrida服务端版本建议15Python环境安装frida-tools目标应用的APK文件用于分析类结构我在Ubuntu 20.04上实测时发现frida-server的版本必须与客户端一致。曾经因为版本不匹配导致注入失败折腾了半天才发现是这个原因。2.2 基础环境配置先启动frida-serveradb push frida-server /data/local/tmp/ adb shell chmod 755 /data/local/tmp/frida-server adb shell /data/local/tmp/frida-server 验证是否正常工作frida-ps -U看到进程列表说明环境就绪。建议每次重启设备后都检查服务是否正常运行我有次忘记启动服务导致脚本死活不生效。3. 拦截HttpURLConnection请求3.1 核心实现原理HttpURLConnection是Android原生的HTTP客户端。通过Frida替换其关键方法实现拦截Java.perform(function () { var HttpURLConnection Java.use(java.net.HttpURLConnection); HttpURLConnection.getInputStream.implementation function () { // 获取请求信息 var url this.getURL().toString(); var method this.getRequestMethod(); console.log([] Request: method url); // 打印请求头 var headers this.getRequestProperties(); for (var key in headers) { console.log([] Header: key : headers[key]); } // 获取原始输入流 var originalStream this.getInputStream(); return originalStream; }; });这个基础版本能打印请求行和请求头。但实际使用时发现有些POST请求的body获取不到需要额外处理输出流。3.2 处理POST请求体改进后的body捕获逻辑if (this.getDoOutput()) { var outputStream this.getOutputStream(); var writer new Java.use(java.io.OutputStreamWriter)(outputStream); var body Java.use(android.util.Log).getStackTraceString( Java.use(java.lang.Throwable).$new() ); console.log([] Body: body); }这里用了个小技巧通过异常堆栈获取当前线程的调用栈间接提取正在传输的数据。在测试某电商App时这个方法成功捕获了商品搜索的关键词。4. 拦截OkHttp请求4.1 OkHttp拦截器机制OkHttp的设计更现代本身就支持拦截器链。我们可以注册自定义拦截器var MyInterceptor Java.registerClass({ name: com.custom.MyInterceptor, implements: [Interceptor], methods: { intercept: function (chain) { var request chain.request(); // 打印请求信息 console.log([] OkHttp Request: request.method() request.url()); // 处理响应 var response chain.proceed(request); var responseBody response.body().string(); console.log([] Response: responseBody); return response; } } });注意这里有个坑response.body().string()只能调用一次之后流就关闭了。需要重新构建响应对象var newResponse response.newBuilder() .body(ResponseBody.create(null, responseBody)) .build();4.2 动态注入拦截器通过重写Builder的build方法实现自动注入OkHttpClientBuilder.build.overload().implementation function () { this.addInterceptor(MyInterceptor.$new()); return this.build(); };在分析某社交App时发现它用了多个OkHttpClient实例。这时就需要遍历所有实例进行注入不能只拦截默认构建器。5. 实战技巧与常见问题5.1 性能优化建议过滤无关请求添加URL白名单避免日志爆炸异步处理耗时操作放到独立线程缓存响应避免重复处理相同请求我常用的URL过滤代码if (!url.contains(api.target.com)) { return originalMethod.apply(this, arguments); }5.2 疑难问题排查遇到过最棘手的问题是类加载时序。有次拦截失败后来发现是OkHttp库还没加载。解决方法setTimeout(function() { Java.perform(function() { // 实际拦截代码 }); }, 3000);另一个常见问题是混淆代码。建议先用jadx反编译APK确认关键类和方法名。某次分析银行App时HttpURLConnection被混淆成了a.b费了好大劲才定位到。6. 高级应用场景6.1 修改请求参数不仅能拦截还能实时修改。比如测试越权漏洞时var newRequest request.newBuilder() .header(Authorization, Bearer fake_token) .build(); return chain.proceed(newRequest);6.2 自动化测试集成结合Python实现自动化import frida def on_message(message, data): print(message) session frida.get_usb_device().attach(com.target.app) with open(interceptor.js) as f: script session.create_script(f.read()) script.on(message, on_message) script.load()这个方案在我们团队的CI流程中运行良好每天自动检测接口变更。