<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Android Root on Code is cheap, let&#39;s talk</title>
    <link>https://blog.ferstar.org/series/android-root/</link>
    <description>Code is cheap, let&#39;s talk</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>zh-CN</language>
    <copyright>© 2026 ferstar · [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh)</copyright>
    <lastBuildDate>Sun, 11 Oct 2026 21:45:00 +0800</lastBuildDate>
    <ttl>60</ttl><atom:link href="https://blog.ferstar.org/series/android-root/index.xml" rel="self" type="application/rss+xml" /><image>
      <url>https://blog.ferstar.org/site-logo.png</url>
      <title>Code is cheap, let&#39;s talk</title>
      <link>https://blog.ferstar.org/</link>
    </image>
    
    <item>
      <title>它不走 VPN，也不许你开：雅迪的 VPN 检测是怎么治住的</title>
      <link>https://blog.ferstar.org/posts/how-to-avoid-yadea-vpn-detection/</link>
      <pubDate>Sun, 11 Oct 2026 21:45:00 +0800</pubDate>
      
      <guid isPermaLink="true">https://blog.ferstar.org/posts/how-to-avoid-yadea-vpn-detection/</guid>
      <description>开着 VPN 打开雅迪 App 就弹「检测到VPN环境，请调整网络」然后直接退出；app 套了 360 加固还带反 Hook，上一篇文章那套进程内诊断全失灵。实测更气人：雅迪的流量根本不走 VPN，它照样管你全机的网络。最后用 VPN Hide 在 system_server 的 Binder 层和内核 kmod 层只对雅迪隐藏 VPN 特征，冷启动恢复正常——检测在它看不见的层级被拦下。</description><content:encoded><![CDATA[<p><a href="/posts/how-to-avoid-wps-root-detection/" >WPS 的 root 提示</a>刚处理完，又轮到雅迪。手机开着 VPN 的时候打开雅迪 App，会弹一句「检测到VPN环境，请调整网络」，然后 App 直接退出，车况页面都进不去。</p>
<p>WPS 好歹只是提醒一下，雅迪是直接不让用。更离谱的是，雅迪自己的流量根本不走 VPN，我开不开 VPN 跟它的网络没关系，它就是要管。</p>

<h2 class="relative group">现象
    <div id="现象" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%8e%b0%e8%b1%a1" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>logcat 里很清楚：主进程打出「检测到VPN环境,请调整网络」，接着自己退出。<code>:pushservice</code> 推送进程也会弹同样的提示。</p>
<p>本来想拆 APK 看看，结果雅迪 8.8.12 套了 360 加固，拆出来只有壳代码，业务逻辑看不到。</p>

<h2 class="relative group">进程内 hook 被加固拦住
    <div id="进程内-hook-被加固拦住" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%bf%9b%e7%a8%8b%e5%86%85-hook-%e8%a2%ab%e5%8a%a0%e5%9b%ba%e6%8b%a6%e4%bd%8f" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>WPS 那次是写个临时的 LSPosed 诊断模块，进进程 hook 检测方法、记录返回值。同样的办法放到雅迪上，加固壳初始化时就发现了，App 反复退出，走不到业务代码；把模块撤掉，又变回原来的 VPN 提示。进程内 hook 这条路走不通。试了下 Frida，版本兼容有问题，也没跑起来。</p>

<h2 class="relative group">内存里 dump 出来的代码
    <div id="内存里-dump-出来的代码" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%86%85%e5%ad%98%e9%87%8c-dump-%e5%87%ba%e6%9d%a5%e7%9a%84%e4%bb%a3%e7%a0%81" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>静态拆不开，就等它运行时自己解密。正常启动后从内存里 dump 出业务 DEX，就能看到代码了。</p>
<p>顺着网络检测找，在个推 SDK 里有一段枚举网卡的代码，检查 <code>tun0</code>、<code>ppp0</code> 这类 VPN 网卡。不过没有证据证明那条提示就是它弹的，具体是哪个 API 触发的也没抓到，这里不下结论。</p>
<p>能确定的是另一件事。雅迪的 UID 是 <code>10450</code>，不在 VPN 接管的 UID 列表里，VPN 客户端的分应用名单里也没有它，它的流量本来就是直连的。所以在 VPN 客户端里把雅迪设成绕过也没用，它查的是整台手机有没有 VPN，跟自己的流量走哪条路无关。WPS 查的是装了什么应用，雅迪管的是整台手机的网络状态，伸得更远。</p>

<h2 class="relative group">换到进程外面处理
    <div id="换到进程外面处理" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%8d%a2%e5%88%b0%e8%bf%9b%e7%a8%8b%e5%a4%96%e9%9d%a2%e5%a4%84%e7%90%86" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>进程里动不了，就只能在它看不到的地方改数据。<a href="https://github.com/okhsunrog/vpnhide"  target="_blank" rel="noreferrer">VPN Hide</a> 就是这么做的，分两层：</p>
<ul>
<li><strong>Java 层</strong>：LSPosed 模块挂在 system_server 上，在 Binder 的 <code>writeToParcel</code> 处把 <code>NetworkCapabilities</code>、<code>NetworkInfo</code>、<code>LinkProperties</code> 里的 VPN 信息去掉，App 通过 IPC 拿到的网络数据里就没有 VPN。整个过程不注入雅迪进程，加固的反 Hook 和内存完整性检测发现不了。</li>
<li><strong>原生层</strong>：用内核模块（kmod，基于 kprobe/kretprobe）在内核里隐藏 <code>tun</code> 网卡，接口枚举的 ioctl、<code>getifaddrs</code>、netlink 路由转储都会过滤，native 代码也查不到。后端可以选 kmod、KPM 或 Zygisk，Zygisk 是在进程内做 inline hook，可能被加固识破，这台手机的内核支持 kmod，就用了 kmod。</li>
</ul>
<pre class="not-prose mermaid">
flowchart TD
    Y["雅迪 App（360 加固 + 反 Hook）"]
    Y -->|"进程内 Hook 注入"| X["加固检测到介入，反复退出 ✗"]
    Y -->|"Java API 查询网络状态"| S["system_server<br/>VPN Hide：Binder 层过滤网络数据 ✓"]
    Y -->|"native 枚举网卡、路由"| K["内核<br/>VPN Hide kmod：藏起 tun0 ✓"]
</pre>

<p>kmod 不用刷内核，也不改 boot 镜像，是 KernelSU 模块开机时往现有内核里加载一个 <code>.ko</code>。但它确实改了运行中内核的行为，不兼容的话可能导致系统崩溃，卸载后也要再重启一次才能完全恢复。</p>
<p>配置只针对 <code>com.yadea.smartmoto</code> 开启 Java 和原生两层隐藏，LSPosed 作用域只勾系统框架，不勾雅迪。</p>

<h2 class="relative group">结果
    <div id="结果" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e7%bb%93%e6%9e%9c" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>重启加载内核模块后，NekoBox 保持连接，雅迪连续两次冷启动都正常进了车辆首页，没再弹 VPN 提示，也没退出。</p>
<p>VPN Hide 有个统计页，开着抓了 43 秒，期间冷启动一次雅迪，一共记录到 156 次拦截：</p>
<table>
  <thead>
      <tr>
          <th>查询类型</th>
          <th>次数</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>原生 ioctl 网络接口查询</td>
          <td>130</td>
      </tr>
      <tr>
          <td>IPv4 / IPv6 地址枚举</td>
          <td>各 3</td>
      </tr>
      <tr>
          <td>Java <code>NetworkInfo</code></td>
          <td>12</td>
      </tr>
      <tr>
          <td>Java <code>Network</code> 句柄</td>
          <td>5</td>
      </tr>
      <tr>
          <td>Java <code>ConnectivityService</code></td>
          <td>3</td>
      </tr>
  </tbody>
</table>
<p>大头是原生层的 ioctl 接口查询，这条路能拿到接口列表、地址和启用状态，足够发现 <code>tun0</code> 这类 VPN 网卡。只在 Java 层过滤的话，这部分查询是拦不住的，这也是要同时开 kmod 的原因。不过这些计数里也包含正常的网络调用，不能理解成雅迪专门查了 156 次 VPN，也没法靠统计确定弹警告的是哪一次调用。</p>
<p>VPN Hide 自己的状态页显示「无需检查」，看着像没生效，其实是因为它本身不走 VPN，显示这个是正常的。</p>
<p>VPN Hide 只处理手机上能查到的 VPN 特征，不隐藏 root，也不管 Play Integrity。服务端那边的检测，比如 DNS 泄漏、IP 黑名单、TLS 指纹，它管不了。</p>

<h2 class="relative group">和 WPS 对比
    <div id="和-wps-对比" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%92%8c-wps-%e5%af%b9%e6%af%94" aria-label="锚点">#</a>
    </span>
    
</h2>
<table>
  <thead>
      <tr>
          <th></th>
          <th>WPS</th>
          <th>雅迪</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>检测什么</td>
          <td>装没装 root 管理器</td>
          <td>手机上有没有 VPN</td>
      </tr>
      <tr>
          <td>检测代码有没有保护</td>
          <td>没有</td>
          <td>加固 + 反 Hook</td>
      </tr>
      <tr>
          <td>查到之后</td>
          <td>弹 Toast</td>
          <td>弹提示后退出</td>
      </tr>
      <tr>
          <td>在哪一层解决</td>
          <td>包可见性（HMA）</td>
          <td>system_server + 内核（VPN Hide）</td>
      </tr>
  </tbody>
</table>
<p>WPS 的检测就在应用层，没有保护，用 HMA 隐藏包名就够了。雅迪的检测藏在加固后面，进程里动不了，只能往下到 system_server 和内核去改它拿到的数据。</p>
<p>脱壳、dump 内存、读反编译代码、挑内核模块，这一套放在以前，就算是专业人士，为了一个厂商 App 的不合理管制花这么多工夫也不太值，普通用户更是只能忍着。现在有 AI 帮忙，普通人也能把这些活干完，自己的手机想怎么用，总算能自己说了算。</p>
]]></content:encoded>
      
    </item>
    
    <item>
      <title>你关的是开关，它开的是名单：WPS 是怎么查到我 Root 的</title>
      <link>https://blog.ferstar.org/posts/how-to-avoid-wps-root-detection/</link>
      <pubDate>Sun, 11 Oct 2026 17:50:00 +0800</pubDate>
      
      <guid isPermaLink="true">https://blog.ferstar.org/posts/how-to-avoid-wps-root-detection/</guid>
      <description>root 手机上每次打开 WPS 都弹「设备已Root」警告，先写了个 LSPosed 模块精确拦截这条 Toast 还发布到了模块仓库；反编译挖出六项检测链，进程内实测命中的是 KernelSU 包名查询——明明拒绝了「读取应用列表」权限，WPS 靠 Manifest 里的 声明照样查得到；最后用 Hide My Applist 对 WPS 隐藏管理器包名，检测在源头失效。</description><content:encoded><![CDATA[<p>买了个手机，不 root 感觉跟租的一样，所以我 root 了。结果 WPS 不知道从哪个版本开始，手越伸越长，连我 root 没 root 都要管：每次从桌面打开它，首页先弹一句「设备已Root，使用WPS Office可能存在安全风险。」一秒就消失，连通知历史都不留。最恶心的是，我明明拒绝了它的「读取应用列表」权限，它还是检测到了我装的 KernelSU。</p>

<h2 class="relative group">这句警告为什么关不掉
    <div id="这句警告为什么关不掉" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%bf%99%e5%8f%a5%e8%ad%a6%e5%91%8a%e4%b8%ba%e4%bb%80%e4%b9%88%e5%85%b3%e4%b8%8d%e6%8e%89" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>这东西压根没走通知栏，是进程里直接调用的 Toast，通知权限管不着，通知历史不留痕，WPS 设置里也翻不到开关。把 KernelSU 对 WPS 的「卸载模块」打开，照弹不误。</p>
<p>后来看了检测代码才知道为什么没用：这台手机根本不是因为 su 文件或挂载点被查出来的，动文件层自然白搭。不过当时还不知道，只想先让这句话闭嘴。</p>

<h2 class="relative group">先让这一句消失
    <div id="先让这一句消失" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e5%85%88%e8%ae%a9%e8%bf%99%e4%b8%80%e5%8f%a5%e6%b6%88%e5%a4%b1" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>先试了现成的 <a href="https://github.com/h465855hgg/toastblocker"  target="_blank" rel="noreferrer">ToastBlocker</a>，作用域只勾 WPS。警告是没了，「保存成功」这类正常提示也跟着没了。</p>
<p>想拦得准一点，写轮眼（MyAndroidTools）那套组件开关用不上，这句警告不是单独的 Activity、Service 或 Receiver，调用点在首页 <code>HomeControllerImpl$1$1.run()</code> 里，检测完直接调 Toast。只拦这一句，就只能上方法级 Hook，也就是 LSPosed。</p>
<p>于是写了个模块，规则只有一条：整句精确匹配那句警告（忽略排版空白），其他 Toast 照常显示，WPS 的检测结果也不动。实现上通过公开的 <code>Toast.makeText/setText</code> 认内容，标记命中的 Toast 对象，只跳过它的 <code>show()</code>，不去读 Android 16 已经限制访问的隐藏字段 <code>Toast.mText</code>。第一版用 legacy API 在实机上验证过，发布前又迁到 libxposed API 102 的 interceptor chain 重写了一遍。构建、签名、发布都走 GitHub Actions，<a href="https://github.com/ferstar/wps-root-toast"  target="_blank" rel="noreferrer">wps-root-toast</a> 1.0.0 已经进了 <a href="https://github.com/Xposed-Modules-Repo/io.github.ferstar.wpsroottoast"  target="_blank" rel="noreferrer">LSPosed 模块仓库</a>。</p>
<p>警告是不弹了，但检测还在跑，只是看不见结果。</p>

<h2 class="relative group">WPS 怎么判定「已 Root」
    <div id="wps-怎么判定已-root" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#wps-%e6%80%8e%e4%b9%88%e5%88%a4%e5%ae%9a%e5%b7%b2-root" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>把 WPS 26.9.1 拆开，首页用的判定在 <code>KSystemRoot.i(context)</code>，六项检查按顺序走，任何一项命中就返回 true。</p>
<pre class="not-prose mermaid">
flowchart TD
    A["KSystemRoot.i(context)"] --> B1{"PATH 各目录下存在 su 文件？"}
    B1 -->|命中| T["判定已 Root"]
    B1 -->|未命中| B2{"persist.sys.root.status 非空且不为 0？"}
    B2 -->|命中| T
    B2 -->|未命中| B3{"ro.secure 为 0？"}
    B3 -->|命中| T
    B3 -->|未命中| B4{"Build.TAGS 含 test-keys？"}
    B4 -->|命中| T
    B4 -->|未命中| B5{"指纹或型号含模拟器特征？"}
    B5 -->|命中| T
    B5 -->|未命中| B6{"查询到 Root 管理器包名？"}
    B6 -->|命中| T
    B6 -->|未命中| F["判定未 Root"]
    T --> S["首页 Toast：设备已Root，使用WPS Office可能存在安全风险"]
    style B6 fill:#ffcdd2,stroke:#d32f2f,stroke-width:2px
</pre>

<p>另外还有两处独立检测：<code>LogoutTracker.h()</code> 查两个固定路径的 <code>su</code>；<code>GetDeviceInfoHandler.a()</code> 查 <code>/system/bin/su</code> 和 <code>/system/xbin/su</code>，结果放进设备信息的 <code>root</code> 字段，通过 JS 回调返回。光看代码看不出会不会上传到服务器，这里不下结论。</p>
<p>代码只能说明有这六项，看不出我这台手机命中的是哪一项。于是临时改了个诊断版，hook 检测方法本身，只记录每一项的返回值，装上冷启动一次：</p>
<table>
  <thead>
      <tr>
          <th>检测项</th>
          <th>本机实测</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>PATH 各目录下的 <code>su</code> 文件</td>
          <td>false</td>
      </tr>
      <tr>
          <td><code>persist.sys.root.status</code> 属性</td>
          <td>false</td>
      </tr>
      <tr>
          <td><code>ro.secure=0</code></td>
          <td>false</td>
      </tr>
      <tr>
          <td><code>Build.TAGS</code> 含 <code>test-keys</code></td>
          <td>false</td>
      </tr>
      <tr>
          <td>模拟器特征（<code>goldfish</code>、<code>ranchu</code>、<code>sdk_gphone</code> 等）</td>
          <td>false</td>
      </tr>
      <tr>
          <td>查询 <code>me.weishu.kernelsu</code></td>
          <td><strong>true</strong></td>
      </tr>
      <tr>
          <td>最终判定</td>
          <td><strong>true</strong></td>
      </tr>
  </tbody>
</table>
<p>前五项都是 false，命中的是第六项，查 KernelSU 管理器的包名。前面藏 su、改属性之所以没用，也是因为碰的都不是这一项。测完把诊断版卸了，换回正式模块。</p>

<h2 class="relative group">权限被拒，为什么还查得到
    <div id="权限被拒为什么还查得到" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%9d%83%e9%99%90%e8%a2%ab%e6%8b%92%e4%b8%ba%e4%bb%80%e4%b9%88%e8%bf%98%e6%9f%a5%e5%be%97%e5%88%b0" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>我确实没给 WPS「读取应用列表」权限，它照样查到了 KernelSU，因为这是两套机制。</p>
<p>厂商设置里的「读取应用列表」管的是枚举设备上的全部应用。Android 11 之后的包可见性机制允许 App 在 Manifest 的 <code><queries></code> 里点名一批包名，点了名的包不需要任何运行时权限就能查装没装。WPS 的 <code><queries></code> 里就列着 KernelSU、Magisk、APatch，系统的可见性记录也显示这两个包互相可见。所以权限面板上点的「拒绝」，对这种点名查询根本不起作用。</p>
<p>这套判定也配不上「安全风险」这个说法：装了管理器 App 就算命中，根本不看 WPS 自己能不能拿到 root；模拟器特征也算 Root，用模拟器的人大概也想不到会被这么提醒一句。</p>

<h2 class="relative group">让查询返回「未安装」
    <div id="让查询返回未安装" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e8%ae%a9%e6%9f%a5%e8%af%a2%e8%bf%94%e5%9b%9e%e6%9c%aa%e5%ae%89%e8%a3%85" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>既然查的是包名，那让 WPS 查不到这个包就行。可选的做法有三种：</p>
<table>
  <thead>
      <tr>
          <th>方案</th>
          <th>做法</th>
          <th>代价</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>应用列表隐藏类模块</td>
          <td>对 WPS 隐藏指定包名</td>
          <td>多数基于旧 API，新系统兼容性要实测</td>
      </tr>
      <tr>
          <td>扩展现有模块</td>
          <td>在 WPS 进程里拦截包查询，返回「未安装」</td>
          <td>要自己写、自己维护</td>
      </tr>
      <tr>
          <td>改 APK 删 <code><queries></code></td>
          <td>重签名后安装</td>
          <td>影响更新和签名校验，不适合日常</td>
      </tr>
  </tbody>
</table>
<p>本来打算再写一个 API 102 的包可见性过滤模块，后来发现 <a href="https://modules.lsposed.org/module/com.tsng.hidemyapplist/"  target="_blank" rel="noreferrer">Hide My Applist</a>（HMA）干的就是这事，就没必要自己写了。</p>
<p>配置只有两条：建一个黑名单模板，里面只放 <code>me.weishu.kernelsu</code>；应用规则只对 WPS 生效，激进过滤和详细日志都不开。验证时先把「WPS Root 提示隐藏」模块关掉，强停后重开 WPS，没再弹。说明包名查询直接被挡住了，六项检测都返回 false。其他入口如果也用同一套判定，一样会失效，原来的模块只能遮住首页这一处。</p>
<p>HMA 3.8.3 用的是 libxposed API 101，还要求框架允许在系统服务里加载原生库，我这套 Android 16 + LSPosed 2.2.1 上实测正常。</p>

<h2 class="relative group">模块白写了吗
    <div id="模块白写了吗" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%e6%a8%a1%e5%9d%97%e7%99%bd%e5%86%99%e4%ba%86%e5%90%97" aria-label="锚点">#</a>
    </span>
    
</h2>
<p>回头看，确实该先找找现成方案再动手。不过不把六项检测挖出来，也不知道触发点是包名查询，更想不到 HMA 一个模板就能解决。</p>
<p><a href="https://github.com/ferstar/wps-root-toast"  target="_blank" rel="noreferrer">wps-root-toast 仓库</a>现已归档，README 保留了 HMA 的实测配置，源码和已发布 APK 也保留供查阅。</p>
<p>模块也不算完全白写。只想去掉这句提醒、不想改包可见性的话，它还能单独用；诊断版也是在它的代码上改出来的。</p>
<p>反编译和 Hook 都只在自己手机上的 WPS 里做，模块只隐藏提示、不改检测结果，仓库里没有 WPS 的代码。发布说明里也只写了实测过的 Android 16 + WPS 26.9.1 + LSPosed 2.2.1。</p>
<p>后来雅迪也开始管 VPN，而且检测藏在加固后面，处理过程写在<a href="/posts/how-to-avoid-yadea-vpn-detection/" >下一篇</a>。</p>
]]></content:encoded>
      
    </item>
    
  </channel>
</rss>
