
前言在 Android 逆向分析与安全测试领域网络请求调试是永恒的核心课题。传统的抓包工具如 Charles、Fiddler在面对 SSL 证书绑定、自定义加密协议、底层 Native 网络库时往往束手无策。而 Frida 作为一款强大的动态插桩框架能够直接注入到 App 进程中从代码层面拦截和修改网络请求成为逆向工程师的必备利器。本文将从环境搭建开始由浅入深地讲解如何使用 Frida Hook 技术对 Android App 的网络请求进行动态调试覆盖 HttpURLConnection、OkHttp 等主流网络库以及 SSL 绕过、请求篡改、加密参数分析等高级实战技巧。一、环境搭建从零开始配置 Frida1.1 Frida 工作原理Frida 采用 C/S 架构PC 端作为客户端发送指令Android 设备上运行 frida-server 作为服务端二者通过 USB 或 TCP 通信。Frida 利用动态二进制插桩技术在运行时向目标进程注入 JavaScript 脚本实现对 Java 层和 Native 层函数的拦截与修改。1.2 PC 端安装确保已安装 Python 3.7 环境执行以下命令bash运行# 安装稳定版本推荐16.x系列 pip install frida16.2.1 frida-tools12.3.0 # 验证安装 frida --version1.3 Android 端配置步骤 1查看设备 CPU 架构bash运行adb shell getprop ro.product.cpu.abi常见返回值arm64-v8a、armeabi-v7a、x86_64步骤 2下载对应版本 frida-server前往 Frida Releases 下载与 PC 端版本一致的 frida-server。例如 arm64 设备下载frida-server-16.2.1-android-arm64.xz步骤 3推送到设备并启动bash运行# 解压后推送到设备 adb push frida-server-16.2.1-android-arm64 /data/local/tmp/frida-server # 赋予执行权限 adb shell chmod 755 /data/local/tmp/frida-server # 后台启动 adb shell /data/local/tmp/frida-server 步骤 4验证连接bash运行frida-ps -U成功列出设备进程即表示环境搭建完成。1.4 两种启动模式Attach 模式Hook 已运行的进程适合调试运行中状态bash运行frida -U -n 进程名 -l hook.jsSpawn 模式冷启动 App 并注入适合 Hook 初始化逻辑bash运行frida -U -f com.example.app -l hook.js --no-pause二、入门篇Hook HttpURLConnection2.1 为什么从 HttpURLConnection 开始HttpURLConnection 是 Android 系统原生的 HTTP 客户端位于java.net包下几乎所有网络库底层最终都会调用它。掌握它的 Hook 方法是网络调试的基础。2.2 核心 Hook 点表格方法作用Hook 时机connect()建立连接请求发出前getInputStream()获取响应流响应返回后getOutputStream()获取请求流POST 数据写入时2.3 完整 Hook 脚本javascript运行Java.perform(function () { console.log([*] HttpURLConnection Hook 已启动); // Hook URL构造捕获所有请求地址 var URL Java.use(java.net.URL); URL.$init.overload(java.lang.String).implementation function (url) { console.log([URL] url); return this.$init(url); }; // Hook connect方法获取请求信息 var HttpURLConnection Java.use(java.net.HttpURLConnection); HttpURLConnection.connect.implementation function () { console.log(\n 请求发起 ); console.log(URL: this.getURL().toString()); console.log(Method: this.getRequestMethod()); // 打印请求头 var headers this.getRequestProperties(); console.log(请求头:); var keys headers.keySet().toArray(); for (var i 0; i keys.length; i) { var key keys[i]; console.log( key : headers.get(key)); } // 打印调用栈定位代码位置 var stack Java.use(android.util.Log) .getStackTraceString(Java.use(java.lang.Exception).$new()); console.log(调用栈:\n stack); return this.connect(); }; // Hook getInputStream捕获响应 HttpURLConnection.getInputStream.implementation function () { var responseCode this.getResponseCode(); console.log(\n 响应返回 ); console.log(状态码: responseCode); console.log(URL: this.getURL().toString()); return this.getInputStream(); }; });2.4 POST 请求体捕获对于 POST 请求请求体写入getOutputStream()需要通过反射读取输出流内容javascript运行var HttpsURLConnection Java.use(javax.net.ssl.HttpsURLConnection); HttpsURLConnection.getOutputStream.implementation function () { var os this.getOutputStream(); console.log([POST] 准备写入请求体); // 可通过ByteArrayOutputStream包装来捕获写入内容 return os; };注意直接捕获 OutputStream 数据较为复杂实际项目中更推荐 Hook 更高层的 OkHttp 库来获取请求体。三、进阶篇Hook OkHttp 网络库3.1 OkHttp 的重要地位OkHttp 是目前 Android 生态中使用最广泛的 HTTP 客户端Retrofit、Glide 等框架均基于 OkHttp 实现。Hook OkHttp 能够获取最完整、最结构化的请求与响应信息。3.2 核心 Hook 策略OkHttp 有多个 Hook 切入点各有优劣表格Hook 点优点缺点Request.Builder.build()捕获所有请求构建拿不到响应CallServerInterceptor.intercept()请求响应都能拿到依赖内部类路径RealCall.execute()入口级 Hook信息需二次解析3.3 实战脚本完整捕获请求与响应javascript运行Java.perform(function () { console.log([*] OkHttp Hook 已启动); // 捕获请求 var RequestBuilder Java.use(okhttp3.Request$Builder); RequestBuilder.build.implementation function () { var request this.build(); console.log(\n OkHttp 请求 ); console.log(URL: request.url().toString()); console.log(Method: request.method()); // 遍历请求头 console.log(--- Headers ---); var headers request.headers(); for (var i 0; i headers.size(); i) { console.log(headers.name(i) : headers.value(i)); } // 读取请求体 var body request.body(); if (body ! null) { console.log(--- Request Body ---); try { var buffer Java.use(okio.Buffer).$new(); body.writeTo(buffer); console.log(buffer.readUtf8()); } catch (e) { console.log(请求体读取失败: e.message); } } return request; }; // 捕获响应 var ResponseBuilder Java.use(okhttp3.Response$Builder); ResponseBuilder.build.implementation function () { var response this.build(); console.log(\n OkHttp 响应 ); console.log(Code: response.code()); console.log(URL: response.request().url().toString()); // 响应头 console.log(--- Response Headers ---); var headers response.headers(); for (var i 0; i headers.size(); i) { console.log(headers.name(i) : headers.value(i)); } // 响应体注意只能读取一次需克隆 var body response.body(); if (body ! null) { try { var source body.source(); source.request(9223372036854775807); // Long.MAX_VALUE var buffer source.buffer().clone(); console.log(--- Response Body ---); console.log(buffer.readUtf8()); } catch (e) { console.log(响应体读取失败: e.message); } } return response; }; });3.4 拦截器层面 Hook更精准对于混淆过的 AppRequest$Builder可能被重命名。此时可以 Hook 拦截器接口javascript运行var CallServerInterceptor Java.use(okhttp3.internal.http.CallServerInterceptor); CallServerInterceptor.intercept.implementation function (chain) { var request chain.request(); console.log(拦截到请求: request.url()); // 可以在这里修改请求 // var newRequest request.newBuilder().url(新地址).build(); // var response chain.proceed(newRequest); var response chain.proceed(request); console.log(响应状态: response.code()); return response; };四、高级篇SSL 证书绕过与请求篡改4.1 通用 SSL Pinning 绕过绝大多数 App 的 SSL 证书绑定都可以通过 Frida 绕过。以下是通用脚本javascript运行Java.perform(function () { console.log([*] SSL Pinning 绕过脚本已加载); // OkHttp 证书绑定绕过 try { var CertificatePinner Java.use(okhttp3.CertificatePinner); CertificatePinner.check.overload( java.lang.String, java.util.List ).implementation function () { console.log([] 绕过 OkHttp CertificatePinner); return; }; console.log([] OkHttp CertificatePinner 已Hook); } catch (e) {} // HttpsURLConnection 绕过 try { var HttpsURLConnection Java.use(javax.net.ssl.HttpsURLConnection); HttpsURLConnection.setSSLSocketFactory.implementation function () { console.log([] 绕过 HttpsURLConnection SSL); return; }; } catch (e) {} // 信任所有证书 var TrustManager Java.use(javax.net.ssl.X509TrustManager); var SSLContext Java.use(javax.net.ssl.SSLContext); // 构造信任所有证书的TrustManager var TrustAllManager Java.registerClass({ name: com.example.TrustAllManager, implements: [TrustManager], methods: { checkClientTrusted: function () {}, checkServerTrusted: function () {}, getAcceptedIssuers: function () { return Java.use(java.security.cert.X509Certificate).array.newInstance(0); } } }); console.log([] SSL 绕过配置完成); });4.2 动态修改请求参数Hook 不仅可以观察还可以修改请求数据javascript运行Java.perform(function () { var RequestBuilder Java.use(okhttp3.Request$Builder); RequestBuilder.url.overload(java.lang.String).implementation function (url) { console.log([原URL] url); // 替换指定接口 if (url.indexOf(/api/login) ! -1) { var newUrl url.replace(/api/login, /api/test_login); console.log([替换URL] newUrl); return this.url(newUrl); } return this.url(url); }; // 修改请求头 RequestBuilder.addHeader.implementation function (name, value) { if (name User-Agent) { value Frida-Hooked/1.0; } return this.addHeader(name, value); }; });4.3 响应数据篡改修改服务器返回的数据常用于绕过服务端校验javascript运行var ResponseBody Java.use(okhttp3.ResponseBody); var BufferedSource Java.use(okio.BufferedSource); var Buffer Java.use(okio.Buffer); // Hook ResponseBody.create 替换响应体 // 实际使用中建议在拦截器层面替换完整Response对象五、精通篇实战技巧与进阶方案5.1 加密参数分析技巧当遇到请求参数加密时按以下步骤定位加密函数Hook 请求体输出密文打印调用栈找到加密方法所在类Hook 加密函数打印明文输入和密文输出必要时主动调用加密函数生成自己的密文示例Hook 常见加密工具类javascript运行Java.perform(function () { // Hook MD5 var MessageDigest Java.use(java.security.MessageDigest); MessageDigest.digest.overload([B).implementation function (input) { var data Java.use(java.lang.String).$new(input); var result this.digest(input); console.log([MD5] input: data); return result; }; // Hook AES加密 var Cipher Java.use(javax.crypto.Cipher); Cipher.doFinal.overload([B).implementation function (input) { var opmode this.getOpMode(); var data bytesToHex(input); var result this.doFinal(input); console.log([AES] mode: opmode input: data); return result; }; }); function bytesToHex(bytes) { var hex ; for (var i 0; i bytes.length; i) { var b bytes[i] 0xFF; hex (b 16 ? 0 : ) b.toString(16); } return hex; }5.2 Native 层网络请求 Hook如果 App 使用 Native 层发起网络请求如 libcurl、直接调用 socket需要 Hook Native 函数javascript运行// Hook send函数libc.so Interceptor.attach(Module.findExportByName(libc.so, send), { onEnter: function (args) { var fd args[0].toInt32(); var buf args[1]; var len args[2].toInt32(); var data Memory.readUtf8String(buf, len); console.log([send] fd fd len len); console.log(data.substring(0, 500)); } }); // Hook recv函数 Interceptor.attach(Module.findExportByName(libc.so, recv), { onLeave: function (retval) { // 读取接收缓冲区数据 } });5.3 Frida 反检测与对抗当 App 检测 Frida 时可采用以下方案修改 frida-server 二进制名避免默认端口使用 frida-gadget注入到 APK 内部Hook 检测函数返回假结果使用Objection框架集成反检测基础反检测脚本javascript运行// 隐藏Frida端口 Interceptor.attach(Module.findExportByName(libc.so, connect), { onEnter: function (args) { // 检测27042端口连接并阻断 } }); // 绕过/proc/pid/maps检测 // 绕过frida特征字符串检测5.4 性能优化与批量 Hook使用Java.use()缓存类对象避免重复查找减少console.log调用使用批量输出针对性 Hook不要全量 Hook 所有方法复杂逻辑使用 Python 端处理JS 端只做数据采集六、常用工具与框架推荐6.1 Objection基于 Frida 封装的高级工具一键完成 SSL 绕过、内存漫游、类搜索等操作bash运行# 安装 pip install objection # 启动并自动SSL绕过 objection -g com.example.app explore6.2 r0capture安卓应用层通杀脚本支持 HttpURLConnection、OkHttp、Volley 等多种网络库bash运行frida -U -f com.example.app -l r0capture.js --no-pause6.3 OkHttpLogger-Frida专门针对 OkHttp 的日志打印工具自动适配不同版本 OkHttp支持混淆识别。七、常见问题排查Failed to spawn: unable to find process检查包名是否正确确认 frida-server 已启动且版本匹配尝试使用-D指定设备 IDClass not found类可能被混淆用 jadx 反编译确认类名检查是否在正确进程中多进程 App使用Java.enumerateLoadedClasses()搜索Hook 不生效确认方法签名正确使用overload指定参数Spawn 模式下冷启动 App 再 Hook检查是否有加固或脱壳问题App 闪退可能触发了反检测机制检查脚本中是否有空指针异常逐步注释定位问题代码八、总结Frida Hook 网络请求是 Android 逆向分析的核心技能从简单的 URL 打印到复杂的加密参数分析再到 Native 层 socket 拦截技术深度层层递进。掌握本文介绍的 HttpURLConnection 基础 Hook、OkHttp 全链路拦截、SSL 证书绕过、请求响应篡改等技术足以应对绝大多数 App 的网络调试需求。学习 Frida 的关键在于实战。建议从简单的 Demo App 入手逐步尝试 Hook 真实商业应用在实践中积累对抗经验。同时要持续关注 Frida 官方更新和社区优秀脚本不断完善自己的工具库。最后提醒Frida 技术仅可用于合法的安全测试与学习研究请勿用于非法用途。