尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
小程序抓包抓不到明文:SSL校验分层与绕过验证
简介该资源聚焦于支付宝小程序 SSL 验证机制的研究与调试场景面向从事移动端抓包分析、接口调试及安全测试的开发者与进阶学习者提供借助 Frida 动态注入与代理工具绕过证书校验、恢复小程序网络流量可见性的实践素材。压缩包共 3 个文件约 2KB体量轻巧包含 1 个 Python 脚本负责注入与钩子逻辑1 个 JavaScript 脚本承担运行时 Hook 与证书校验替换另有 1 份 Markdown 说明文档交代环境准备、依赖安装与执行流程三类文件互为配套形成从脚本运行到结果验证的闭环。资源已被 3043 人学习下载具有一定实践参考基础。读者可从中获得可复用的注入脚本骨架、证书校验拦截思路与代理联调方法并借助说明文档快速复现抓包环境理解脚本各阶段的执行顺序与调试要点为后续自定义 Hook 与问题排查提供可改造的起点。1. 抓不到明文的真正原因小程序 SSL 校验不住在系统信任链里设备上装好了 CA浏览器打开 HTTPS 站点明文一目了然同一个抓包工具挂到小程序上日志里只剩一条 CONNECT 记录后面什么都没有。多数人第一反应是工具版本旧了、证书过期了折腾半天换工具换机器问题照旧。真正的原因通常跟工具无关宿主 App 把服务端证书的校验写进了自己的代码里系统信任区里多放一张 CA 对它完全不起作用。小程序发出的网络请求最终由宿主 App 的原生网络库兜底校验收敛在 OkHttp 的 CertificatePinner、自研网络库里的公钥指纹比对或者容器自己封的一层 TLS 检查上。这篇笔记面向正在做移动端接口联调、自有 App 安全测试、以及要排查小程序链路问题的工程师把校验位置、环境搭建、绕过路径和验证方法按可复现的顺序拆开讲。2. 证书校验的三个落点TrustManager、CertificatePinner 与容器双份校验要做好这件事先得知道对手在哪。移动端的证书校验不是单点它至少分布在三个层次上系统信任链、应用层 Pinning、以及小程序容器自带的网络栈。搞不清层次就会出现“证书装了、Hook 也打了、就是没效果”的情况。下面按从底层到上层的顺序把每一层会在哪里拦你、拦的方式是什么讲清楚。2.1 从 Android 7 的分水岭说起用户证书为什么默认不生效Android 7.0API 24之后应用的默认信任锚只包含系统 CA 目录/system/etc/security/cacerts用户通过设置界面安装的证书落在/data/misc/user/0/cacerts-added默认不被应用信任。这是很多人第一次抓包翻车的地方证书明明在“受信任的凭据”里能看到握手却直接失败。三个变量决定这一步能不能过变量取值影响targetSdkVersion 24默认信任用户证书直接可抓targetSdkVersion 24默认不信任用户证书需额外处理network_security_config未声明 trust-anchors走系统默认策略network_security_config声明了 user 证书用户证书进入信任链查 targetSdkVersion 最直接的办法是看安装包# 从 APK 里读 targetSdkVersionaapt 在 build-tools 目录下 aapt dump badging target.apk | grep -i sdkVersionaapt dump badging会输出sdkVersion:xx和targetSdkVersion:xx两行前者是 minSdk后者才是决定证书策略的那个。如果目标包 targetSdk 是 24 以上且没有配置network_security_config那你把 CA 装到用户区是白装必须走系统区或者改配置。判断证书到底有没有被信任比看设置界面靠得住的办法是直接验证握手# 用 openssl 直连目标域名看服务端证书链和校验结果 openssl s_client -connect api.example.com:443 -servername api.example.com -CAfile test_ca.pem -verify_return_error-CAfile指定你自签的根证书-verify_return_error让校验失败时直接报错而不是继续。如果这里能过、App 里过不了说明问题不在系统信任链往上一层找。这条命令我基本每次搭完环境都会先跑一遍比在 App 里盲试快得多。2.2 应用层 Pinning 的三种常见写法系统信任链过了之后第二道关卡是应用自己写的 Pinning。它绕开了系统的 CA 判断直接比对服务端证书的某个特征值常见有三种写法。第一种是 OkHttp 的CertificatePinner最典型// 应用侧的典型写法把服务端公钥的 SHA-256 钉死在代码里 CertificatePinner pinner new CertificatePinner.Builder() .add(api.example.com, sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) .build();add的第一个参数是域名第二个是sha256/前缀加公钥摘要的 Base64。这种写法的特点是即使你换了 CA、装了系统证书只要服务端公钥没变比对就照样通过反过来说你用自己的抓包工具签发的证书公钥跟它不一样就会被拒。它挂在okhttp3.CertificatePinner.check(String, ListCertificate)上Hook 点非常明确。第二种是自研比对。有些团队不用 OkHttp 的封装自己拿X509Certificate.getPublicKey().getEncoded()算摘要和常量数组比。这种代码的位置不固定可能在任何网络工具类里静态特征是在MessageDigest.getInstance(SHA-256)附近出现证书对象。找它得靠反编译搜关键字光靠通用 Hook 脚本覆盖不到。第三种是双向认证。服务端要求客户端出示证书客户端在SSLContext.init时传入KeyManager同时用自定义的TrustManager校验服务端。这种场景下光有服务端的 CA 不够你还得拿到客户端证书和私钥否则握手在 CertificateRequest 阶段就断了。遇到双向认证讨论“绕过”意义不大正确做法是找服务端要测试环境的客户端证书。2.3 小程序容器里的一笔流量两套网络栈小程序不是独立进程它跑在宿主 App 的容器里。这就带来一个容易被忽略的事实同一个小程序发出的请求可能走两条完全不同的链路。一条是 JS 层调用容器 API比如request这类封装容器把参数交给宿主 App 的原生网络库发出走的是 OkHttp 或自研库校验在 Java/Native 层。另一条是页面里直接用 XHR/fetch 发请求走 WebView 的网络栈校验可能落在WebViewClient.onReceivedSslError这个回调上——默认实现是handler.cancel()你要让它proceed()。这意味着你在 Java 层把 OkHttp 的 Pinning 挂掉了WebView 那条链路可能还在拦反过来也一样。判断一笔请求走哪条链路的方法很朴素看抓包工具里这条连接的 User-Agent 和 TLS 指纹特征WebView 发出的请求 UA 里通常带; wv标记原生库发出的则是 App 自定义的 UA。分不清的时候两条链路都处理一遍成本比反复试错低。另外容器往往有独立进程。多进程 App 里网络请求可能在:container之类的子进程里发出而你的 Hook 脚本只 attach 了主进程日志干干净净你会以为 Hook 没生效。这一点在后面的排查章节还会展开。3. 把抓包环境搭起来CA 签发、系统证书区与可调试测试包原理清楚之后环境搭建是决定成败的一半。这一步的目标很明确让目标设备信任你签发的 CA并且这个信任要发生在系统层而不是用户层。下面三节分别解决证书怎么来、往哪放、测试包怎么配。3.1 抓包工具的 CA 生成与证书链整理抓包工具首次启动时会在配置目录下生成一套 CA包含私钥和证书。以 mitmproxy 为例默认在~/.mitmproxy/下mitmproxy-ca-cert.pem是给设备用的证书mitmproxy-ca.pem是带私钥的完整文件别把后者拷到任何共享位置。拿到证书后先看清它的结构# 查看证书主题、有效期和基本约束 openssl x509 -in mitmproxy-ca-cert.pem -noout -subject -issuer -dates -text | head -30输出里重点看三处Subject和Issuer是否一致自签根证书应该一致、Not After有效期、以及X509v3 Basic Constraints是否为CA:TRUE。有些工具生成的中间证书不带 CA 标记装到系统区也不会被当作信任锚握手照样失败。如果目标应用校验证书链深度还需要把中间 CA 一起准备好。抓包工具签发的是“根 CA → 叶子证书”两段式正常够用个别应用会检查chain.length遇到这种情况在工具里配置上游证书链就行不必自己造。设备侧如果只是临时验证可以用工具自带的二维码或者http://mitm.it页面下载安装但这种方式装的是用户证书仅对 targetSdk 24 的包有效。要长期用直接走系统区。3.2 把 CA 放进系统信任区的三条路径系统证书区的文件名不是随便起的必须按subject_hash_old的前八位十六进制命名后缀.0。算错一位系统就认不出来。# 1. 计算证书的旧版 subject hashAndroid 系统证书区按这个命名 HASH$(openssl x509 -inform PEM -subject_hash_old -in mitmproxy-ca-cert.pem | head -1) echo 证书哈希: $HASH # 2. 按哈希重命名后缀必须是 .0 cp mitmproxy-ca-cert.pem ${HASH}.0 # 3. 推入系统证书区要求设备 system 分区可写 adb root adb remount adb push ${HASH}.0 /system/etc/security/cacerts/ adb shell chmod 644 /system/etc/security/cacerts/${HASH}.0 # 4. 重启后生效 adb reboot-subject_hash_old用的是 OpenSSL 旧版哈希算法这一点不能省用新版-subject_hash算出来的名字 Android 不认。chmod 644也不能漏权限不对的话系统读不到表现为“文件在那儿但证书列表里没有”。三条可行路径按侵入性从低到高排第一条用可写 system 镜像的模拟器。这是最省事的方式adb remount直接成功重启后证书常驻适合反复调试。缺点是模拟器上跑不了带硬件校验或加固的包。第二条真机 系统级证书挂载模块。需要设备已解锁并具备 root 能力模块把证书以 overlay 方式挂到系统目录不改动原始分区卸载方便。这类模块的原理是把证书放到/data下的某个目录启动时 bind mount 到/system/etc/security/cacerts。Android 10 之后系统分区默认只读直接remount基本都会失败overlay 是主流做法。第三条改测试包的network_security_config让应用显式信任你的 CA。这条不需要 root但需要你能重新签名安装包只适用于自有包或允许改包的测试场景下一节细讲。提示证书有效期别设太长。抓包工具的 CA 默认有效期往往好几年一旦私钥泄露影响面很大调试环境建议每次重新生成用完即删。3.3 测试包的构建开关debuggable 与 network_security_config如果你手上有源码或者能改包最干净的做法是让测试包自己信任调试 CA而不是去动系统分区。核心是一个 XML 配置!-- res/xml/network_security_config.xml -- network-security-config !-- 仅 debug 构建生效release 包不受影响 -- debug-overrides trust-anchors certificates srcraw/debug_ca / /trust-anchors /debug-overrides !-- 针对特定域名放宽避免全局放开 -- domain-config domain includeSubdomainstrueapi.example.com/domain trust-anchors certificates srcraw/debug_ca / certificates srcsystem / /trust-anchors /domain-config /network-security-config三个参数的含义debug-overrides只在android:debuggabletrue时生效是给调试包开的后门release 包自动失效raw/debug_ca指向放在res/raw下的证书文件文件名不带扩展名domain-config限定只对指定域名放宽比全局信任安全得多。在AndroidManifest.xml的application上挂这个配置application android:networkSecurityConfigxml/network_security_config android:debuggabletrue构建时用 debug 变体装到设备上就能直接抓到明文。这套配置的价值在于可复现换一台机器、换一个同事只要拿同一份源码构建环境就是一致的不用每台设备都去折腾证书。注意debuggabletrue会打开大量调试能力任何情况下都不要出现在对外发布的包里。CI 里加一条检查扫描 release 产物的 manifest发现 debuggable 为 true 直接让流水线失败。4. 绕过校验的四条路径与选型改包、Hook、系统证书与 Native 打桩环境就绪之后剩下的事就是针对具体校验点下手。四条路径各有适用边界选错了会浪费大量时间改包看着简单但重签名会触发完整性校验Hook 灵活但多进程和 Native 层会漏系统证书最通用但依赖设备权限。逐条拆开讲。4.1 静态改包改配置的收益与重签名的代价能拿到安装包并且允许改动时静态改包是最彻底的方案把上一节的network_security_config塞进去让应用自己信任你的 CA不依赖任何运行时工具。流程用 apktool 走一遍# 1. 反编译保留资源文件 apktool d -f -s target.apk -o target_src # 2. 放入证书与配置文件假设已准备好 res/raw/debug_ca.pem 和 res/xml/network_security_config.xml cp debug_ca.pem target_src/res/raw/ cp network_security_config.xml target_src/res/xml/ # 3. 在 AndroidManifest.xml 的 application 节点加上 networkSecurityConfig 属性 # 用编辑器或 sed 修改注意不要破坏原有属性顺序 # 4. 回编译 apktool b target_src -o target_patched.apk # 5. 重新签名v1 v2 都要签 apksigner sign --ks debug.keystore --ks-pass pass:android \ --v1-signing-enabled true --v2-signing-enabled true \ --out target_signed.apk target_patched.apk # 6. 校验签名是否有效 apksigner verify --verbose target_signed.apk-s参数表示不反编译 dex只处理资源速度快很多如果 Manifest 改动涉及代码逻辑就得去掉-s。apksigner的--v1-signing-enabled和--v2-signing-enabled建议都开Android 7 以下只认 v17 以上优先 v2 但部分场景两者都会检查。改包的最大代价是签名变了。应用启动时会检查自身签名摘要和目标值对不上就直接退出有些包还会校验 APK 的 CRC 或 DEX 的哈希。这类完整性校验的代码位置不固定可能是PackageManager.getPackageInfo拿signatures比对也可能是 Native 层直接读/proc/self/maps算段哈希。遇到闪退先看 logcat 里的异常栈能定位到具体类就直接改定位不到就换动态 Hook 方案别在改包上死磕。还有一层改了签名之后应用内依赖签名一致性的功能会失效比如某些 SDK 的初始化校验、分享回调、支付回调。做接口联调时不涉及这些还好涉及的话得提前准备。4.2 Java 层动态 Hook三个必须挂上的校验点动态 Hook 的优势是不改包、不重签名随时开关。用 Frida 挂 Java 层有几个点是必挂的先做一次类扫描把目标找出来// scan.js —— 只做侦察输出所有可能和证书校验相关的已加载类 Java.perform(function () { Java.enumerateLoadedClasses({ onMatch: function (name) { // 关键字覆盖 TrustManager、Pinning、HostnameVerifier 三类 if (/TrustManager|CertificatePinner|HostnameVerifier|X509/.test(name)) { console.log(name); } }, onComplete: function () { console.log([*] class scan done); } }); });enumerateLoadedClasses列出的是当前进程已加载的类所以脚本必须在 App 启动之后再 attach或者用-f参数在启动时就注入。扫描结果里除了javax.net.ssl.X509TrustManager这类标准接口往往还能看到应用自己写的XXTrustManager后者才是真正要处理的目标。有了目标类再挂实际的方法// bypass.js —— 仅用于自有测试包或已获书面授权的目标 Java.perform(function () { // 1. 自定义 TrustManager把 checkServerTrusted 置空 var TrustManagerImpl Java.use(com.android.org.conscrypt.TrustManagerImpl); TrustManagerImpl.checkTrustedRecursive.implementation function () { console.log([] checkTrustedRecursive bypassed); return Java.use(java.util.ArrayList).$new(); // 返回空链等价于信任 }; // 2. OkHttp CertificatePinner直接返回跳过比对 try { var Pinner Java.use(okhttp3.CertificatePinner); Pinner.check.overload(java.lang.String, java.util.List).implementation function (host, pins) { console.log([] CertificatePinner.check bypassed: host); return; }; } catch (e) { console.log([-] CertificatePinner not present: e.message); } // 3. HostnameVerifier域名不匹配时放行 try { var HNV Java.use(javax.net.ssl.HostnameVerifier); HNV.verify.implementation function (host, session) { console.log([] HostnameVerifier bypassed: host); return true; }; } catch (e) { console.log([-] HostnameVerifier hook failed: e.message); } });三段逻辑分别对应三类校验。checkTrustedRecursive是 Conscrypt 里真正做证书链校验的方法返回空列表等于告诉调用方“链是合法的”CertificatePinner.check的overload必须写清楚参数签名OkHttp 不同版本可能有多个重载签名对不上会抛异常HostnameVerifier.verify返回true是最粗暴的放行但只在自定义了 Verifier 的场景下有效。启动方式有两种效果差别很大# 方式一attach 到已运行的进程App 已经启动可能错过早期初始化 frida -U -n com.example.app -l bypass.js --no-pause # 方式二从启动开始注入推荐能覆盖首次网络请求 frida -U -f com.example.app -l bypass.js --no-pause-f会先启动应用再注入--no-pause让应用继续运行。多进程应用要额外注意-f默认只注入主进程子进程需要用frida -U -f com.example.app -l bypass.js配合脚本里的Java.perform在子进程运行时再触发或者在脚本里用Process.enumerateThreads判断当前进程名再决定是否执行 Hook 逻辑。实际项目里更省事的做法是写一个启动脚本先frida-ps -U列出所有相关进程逐个 attach。4.3 Native 层BoringSSL 的校验入口怎么找当校验逻辑下沉到 Native 层Java Hook 就完全无效了。这类实现通常是把 BoringSSL 静态链接进自己的 so或者用SSL_CTX_set_custom_verify注册自定义校验回调。前者导出符号可能被裁剪后者干脆没有符号。先尝试用导出符号定位// native_bypass.js —— 在 libssl.so 里拦截证书链校验 Java.perform(function () { // 部分应用把 BoringSSL 编成独立 so导出 SSL_verify_cert_chain var addr Module.findExportByName(libssl.so, SSL_verify_cert_chain); if (addr null) { console.log([-] SSL_verify_cert_chain not exported, 需要走特征码定位); return; } Interceptor.replace(addr, new NativeCallback(function (ssl, store_ctx) { console.log([] SSL_verify_cert_chain forced to success); return 1; // BoringSSL 约定返回 1 表示校验通过 }, int, [pointer, pointer])); });SSL_verify_cert_chain的返回约定是 1 成功、0 失败替换成常量 1 就等于永远通过。Module.findExportByName找不到时返回null所以必须先判空再替换否则脚本会直接崩。找不到导出符号的情况更常见。这时候的定位手段是搜特征BoringSSL 的校验失败路径上通常会有固定的错误码字符串比如certificate verify failed、X509_V_ERR_前缀的日志用strings扫 so 文件找到字符串偏移再用 IDA 反查引用它的函数拿到函数起始地址后用 Frida 的Module.base.add(offset)计算运行地址。# 在 so 文件里搜证书校验相关的错误字符串定位可能的失败分支 strings libnative-lib.so | grep -iE certificate|verify|ssl_这条命令的输出能给出一批候选字符串配合反汇编工具交叉引用通常能在半天内定位到校验函数。这活儿不轻松但如果目标是加固过的包这是绕不开的一步。注意Native 层替换函数时参数列表必须和原函数完全一致。参数个数或类型对不上Frida 不会报错但原逻辑会拿到错误的寄存器值表现为随机崩溃排查成本极高。拿不准就先只做Interceptor.attach打日志确认签名对了再替换返回值。4.4 四条路径的对照与选型把前面几条路径摊开对比选型时按“有没有源码 / 有没有 root / 校验在哪层”三个问题走路径前置条件适用校验层主要副作用可复现性改 network_security_config能改包、能重签名系统信任链签名变更触发完整性校验高配置随包走系统证书区挂载设备可 root 或可写 system系统信任链部分应用检测 root 环境中依赖设备Java 层 Hook可注入进程Java Pinning、TrustManager多进程需逐个注入中脚本可版本化Native 层打桩可注入 能定位函数自研 SSL 校验符号定位成本高低依赖包版本实际项目里很少只用一条。常见组合是先挂系统证书解决系统信任链再用 Java Hook 解决 OkHttp 的 Pinning最后看日志里还有没有 TLS 层报错有的话再上 Native。按这个顺序推进每步都有明确的验证信号比一上来就堆全套脚本更容易定位问题。5. 常见问题排查五条踩过才信的记录下面五条是这类调试里出现频率最高的问题每条按现象、原因、解决三段写。遇到问题先按这个清单对一遍能省下大量试错时间。5.1 证书装上了握手还是失败现象证书已经推进/system/etc/security/cacerts重启后在系统证书列表里能看到但抓包工具里依然只有 CONNECT 记录没有任何解密后的请求。原因三种可能。一是文件名算错用了新版subject_hash而不是subject_hash_old系统按哈希查找时匹配不到二是文件权限不是 644系统进程读不到三是目标应用根本不看系统信任链它用的是自己打包进 so 的 CA 列表做 Pinning系统区放什么它都不认。解决先验证证书本身。用openssl s_client直连目标域名并指定-CAfile能过说明证书没问题把证书文件从设备上拉回来用openssl x509 -in确认内容完整、不是零字节检查文件名和权限。这三步都过了还失败直接判定为应用层 Pinning跳到动态 Hook。5.2 Hook 打上了流量还是密文现象Frida 脚本输出[] CertificatePinner.check bypassed日志里确实打出了这行但抓包工具里依然看不到明文。原因最常见的是多进程。日志里那行是在主进程打出来的真正发请求的是:container或者:push子进程脚本压根没注入进去。第二种可能是流量走了 WebView 而不是原生库onReceivedSslError这条链路没处理。第三种是 UDP 上的 QUIC抓包工具默认不解密 QUIC只能看到密文 UDP 包。解决用frida-ps -U列出目标应用的所有进程确认请求来自哪个。多进程就针对子进程单独跑脚本或者用frida -U -f 包名加脚本内的进程判断逻辑统一处理。怀疑 WebView 的话在 Java 层挂android.webkit.WebViewClient的onReceivedSslError让它调handler.proceed()。怀疑 QUIC 的话在设备上临时丢弃 UDP 443强制客户端回落到 TCP# 在测试设备上丢弃去往 443 的 UDP 包迫使 QUIC 回落到 TCP 上的 TLS adb shell su -c iptables -A OUTPUT -p udp --dport 443 -j DROP # 调试结束后记得清理规则 adb shell su -c iptables -D OUTPUT -p udp --dport 443 -j DROP这条规则只对当前设备生效重启后自动失效。iptables需要 root非 root 设备可以改抓包工具配置或者直接在测试包里禁用 QUIC。5.3 明文抓到了接口却返回签名错误现象请求体明文可见重放或者稍微改一个参数服务端返回签名校验失败错误码通常是 401 或者自定义的业务码。原因移动端接口普遍带请求签名签名的输入通常包括方法、路径、参数、时间戳、随机数有些还会把设备指纹或者客户端证书摘要算进去。你改了参数但没重算签名服务端一比对就露馅。时间戳偏差超过阈值也会失败重放几分钟前的请求同样会被拒。解决调试阶段只读不改。查看请求结构和字段含义不需要修改任何参数抓下来看就行。确实需要改参数验证业务逻辑时把签名算法从客户端还原出来——搜代码里的hmac、sha256、sign关键字找到拼接顺序和密钥来源。这一步的工作量视实现而定密钥硬编码在包里的话还能拿到走服务端下发或者白盒加密的就得另想办法。日常联调优先用测试环境的固定 token绕开签名环节。5.4 重打包后的应用启动即闪退现象apktool 回编译、重新签名、安装点图标秒退logcat 里一行SecurityException或者干脆没有任何异常栈。原因签名校验。应用启动时读自己的签名摘要和目标值比对不一致直接System.exit。有些包不在 Java 层退出而是在 Native 层检测/data/app下的 APK 路径发现签名文件变了就触发保护逻辑。还有一种情况是 v2 签名缺失Android 7 以上安装时直接拒绝。解决先确认签名完整用apksigner verify --verbose检查 v1/v2/v3 是否都通过。签名没问题还闪退看 logcat 里的完整异常栈grep -i signature\|SecurityException\|verify。定位到具体类就在那个类的方法上挂 Hook 返回正确值定位不到就放弃改包路线改用动态 Hook成本更低。5.5 抓到的响应是乱码或者被截断现象响应体看起来是二进制或者长度明显小于预期部分字段显示为乱码。原因三种。一是响应经过压缩gzip/br抓包工具没有自动解压二是业务层自己做了加密返回的是密文需要客户端解密后才可读三是分块传输chunked中间被截断工具配置了最大 body 大小。解决先看响应头里的Content-Encoding和Content-Typegzip的话在工具里开自动解压。响应体是密文的话找客户端解密函数通常在网络回调里紧接着解析 JSON 之前Hook 那个函数的入参就能拿到明文比逆向加密算法省事得多。body 截断就调大工具的 body 大小限制mitmproxy 里对应--set body_size_limit10m这样的配置项。6. 验证绕过是否生效三组对照与一份自检清单绕过做完不等于生效。我见过太多次“日志显示 Hook 成功实际流量还是密文”的情况所以每次收尾都要做对照验证。三组对照缺一不可。第一组开关对照。同一个请求Hook 关闭时发一次Hook 打开时再发一次对比抓包工具里的记录。关闭时应该只有 CONNECT打开时应该能看到完整的 HTTP 报文。如果两次表现一样说明 Hook 根本没作用到这条链路上别急着往下走。第二组链路对照。同一个小程序页面触发一次原生库请求、一次 WebView 请求分别确认两条链路都能抓到。判断方法看 UA 里的; wv标记或者 TLS 指纹差异。只抓到一条说明另一条链路的校验还没处理。第三组时间对照。App 冷启动后立刻发一次请求再在运行五分钟后发一次。有些应用把证书校验放在首次初始化里启动时注入的 Hook 能覆盖有些放在懒加载的模块里五分钟后的请求走的是另一条代码路径这时候才暴露问题。三组都过了再跑一段自检脚本确认运行时状态// selfcheck.js —— 确认关键 Hook 点当前是否处于被替换状态 Java.perform(function () { var checks [ [okhttp3.CertificatePinner, check], [javax.net.ssl.HostnameVerifier, verify] ]; checks.forEach(function (item) { try { var Cls Java.use(item[0]); // hasOwnProperty 为 true 说明该方法已被脚本接管 var hooked Cls[item[1]].hasOwnProperty(implementation); console.log((hooked ? [] : [-] ) item[0] . item[1]); } catch (e) { console.log([!] item[0] 未加载); } }); });这段脚本只做状态确认不做任何绕过动作。hasOwnProperty(implementation)检查的是 Frida 是否为该方法挂上了替换实现比看控制台日志可信因为日志可能来自上一次运行的残留输出。还有一件事值得单独说把整套流程脚本化。环境搭建的每一步——算哈希、推证书、重启、注入、验证——都写成 shell 脚本参数化包名和域名。我最初嫌麻烦每次手动敲结果有次在测试机上漏了chmod 644排查了两个小时才发现是权限问题。从那以后我每次搭环境都强制走一遍脚本跑完自动执行三组对照输出一份结果清单存档。后来团队里其他人接手同样的调试任务直接改两个变量就能跑省下的沟通成本远超写脚本的时间。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

Windows Server 2012/2016 RAID驱动加载与DISM注入

Windows Server 2012/2016 RAID驱动加载与DISM注入

简介:面向在 Windows Server 2012 R2 和 2016 64 位环境中部署、维护 530 与 930 系列 RAID 阵列卡的服务器管理员和运维人员,这份驱动集合用于解决系统无法正确识别阵列卡、RAID 功能不可用以及存储性能难以正常发挥等问题,适合物理服务器、…

📅 2026/11/6 2:08:06
530/930 RAID阵列卡驱动:Windows Server装机与DISM注入排错

530/930 RAID阵列卡驱动:Windows Server装机与DISM注入排错

简介:面向在 Windows Server 2012 R2 与 2016 环境中维护 530、930 系列阵列卡的管理员和运维人员,这套驱动集合用于解决系统无法正确识别阵列卡、RAID 功能不可用及安装部署阶段的兼容性问题,也可作为服务器重装或磁盘阵列排障时的驱动备份。…

📅 2026/11/6 2:08:06
Java Swing森林冰火人单人版小游戏源码:瓦片地图与AABB碰撞解析

Java Swing森林冰火人单人版小游戏源码:瓦片地图与AABB碰撞解析

简介:这份Java小游戏项目以森林冰火人单人版为主题,面向Java初学者、课程设计学生及游戏开发入门者,帮助读者通过一个可运行的案例理解图形界面、事件处理与基础游戏逻辑。压缩包共78个文件,约2.98MB,包含6个java源码、…

📅 2026/11/6 2:08:06
MORE NEWS

更多资讯

📰

Oracle迁移KingbaseES实战指南与优化策略

1. Oracle迁移KingbaseES项目概述作为国产数据库领域的标杆产品,KingbaseES凭借其出色的Oracle兼容特性,已经成为企业级数据库国产化替代的首选方案之一。在实际项目中,我们经常需要将运行多年的Oracle系统迁移至KingbaseES平台。这种迁移不仅…

📰

iOS 自定义通知 UI:UNNotificationContentExtension 从配置到排错落地

做内容类、工具类应用的同学大概都遇到过这种尴尬:一条推送本该是“封面图 标题 两个快捷按钮”的组合,结果在通知中心里只能显示成一行标题加一行正文,剩下的信息全被折叠掉了。iOS 10 引入的 UserNotifications 框架里,有一块…

📰

基于Spring Boot的校园食堂订餐系统设计与实现:从毕业设计到二次开发

简介:这份资源是面向计算机专业学生与Java Web开发学习者的毕业设计完整文档,聚焦疫情背景下校园食堂就餐拥挤、外卖不健康、点餐不便等现实痛点,给出了一套可落地的订餐系统设计方案。文档围绕Spring Boot轻量框架与B/S架构展开,…

📰

计算机处理器笔记指南:流水线、缓存与性能实测

1. 先搞清楚"计算机处理器笔记"到底该记什么"计算机处理器笔记"这个文件夹名,我在自己的目录里见过太多次。第一次建它的时候,里面塞满了从各处抄来的名词:流水线、超标量、乱序执行、缓存一致性、分支预测、超线程……每…

📰

电力系统潮流计算与开断分析:牛顿-拉夫逊法实践

1. 电力系统潮流计算基础与开断潮流概述电力系统潮流计算是分析电网运行状态的核心工具,它通过求解节点功率平衡方程来确定系统中各节点的电压幅值、相角以及支路功率分布。牛顿-拉夫逊法因其二次收敛特性成为最常用的潮流计算方法,而开断潮流分析则是在…

📰

MySQL外键约束报错分析与最佳实践

1. 外键约束报错问题深度解析今天在重构一个学生成绩管理系统时,遇到了一个让我抓狂的外键约束报错。错误提示明确告诉我"Missing unique key for constraint score_ibfk_9 in the referenced table student",但奇怪的是我明明已经移除了所有相…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬