你关掉开关,它却打开了列表:WPS 如何知道我的手机被 Root 了
内容提要
WPS 通过六项检测判断手机是否 Root,真正触发警告的是查询 KernelSU 包名,而非 su 文件或系统属性。该查询利用 Android 11 包可见性机制,无需读取应用列表权限。作者先写 LSPosed 模块隐藏警告 Toast,后发现用 Hide My Applist 屏蔽包名查询即可让检测全部失效,模块遂归档。
延伸解读
检测机制的关键:包名查询而非文件扫描
WPS 的 Root 检测包含六项检查,但实际触发警告的是查询 KernelSU 包名。前五项(su 文件、系统属性、Build.TAGS、模拟器特征)均未命中,说明传统隐藏手段如卸载 su 或修改属性对此无效。这提醒我们,现代 Root 检测更依赖包可见性机制,而非文件系统痕迹。
权限拒绝为何无效:包可见性机制解析
用户拒绝“读取应用列表”权限后,WPS 仍能检测到 KernelSU,因为 Android 11 引入的包可见性允许应用在 Manifest 中声明 <queries> 来查询特定包,无需运行时权限。WPS 声明了 KernelSU、Magisk 等包名,因此权限面板的拒绝无法阻止此类查询。这解释了为何权限控制在此场景下失效。
更优解决方案:Hide My Applist 屏蔽包名查询
作者最初编写 LSPosed 模块仅隐藏警告 Toast,但检测仍在运行。后发现使用 Hide My Applist (HMA) 屏蔽 WPS 对 KernelSU 包名的查询,可使六项检测全部返回 false,警告彻底消失。HMA 配置简单,且覆盖所有使用同一检测的入口,比仅隐藏 Toast 的模块更彻底。
模块归档的启示:先搜索现有方案
作者反思应优先寻找现有解决方案,而非直接开发模块。虽然 wps-root-toast 模块已归档,但其 README 保留了测试过的 HMA 配置,源码和 APK 仍可供参考。该模块仅隐藏 Toast 而不改变检测结果,适用于只想消除警告的用户。这体现了在 Root 对抗中,理解检测原理比盲目隐藏更重要。
Q&A
WPS 是如何检测手机是否被 Root 的?
WPS 在主页调用 KSystemRoot.i(context) 方法,依次执行六项检查:1. PATH 目录中是否存在 su 文件;2. persist.sys.root.status 属性是否被设置且不为 0;3. ro.secure 是否为 0;4. Build.TAGS 是否包含 test-keys;5. 指纹或型号是否显示模拟器特征;6. 是否能查询到 Root 管理器包名。任意一项命中即判定为已 Root。
为什么我拒绝了 WPS 的“读取应用列表”权限,它还是能检测到 KernelSU?
因为 WPS 使用的是 Android 11 引入的包可见性机制,而非“读取应用列表”权限。WPS 在 Manifest 的 <queries> 中声明了 KernelSU、Magisk、APatch 等包名,系统允许它直接查询这些指定包是否安装,无需任何运行时权限。因此拒绝“读取应用列表”权限对这类查询无效。
如何让 WPS 不再显示“设备已Root”的警告?
可以使用 Hide My Applist (HMA) 模块,配置一个仅包含 me.weishu.kernelsu 的黑名单模板,并针对 WPS 应用该规则,开启激进过滤。这样 WPS 查询 KernelSU 包名时会返回“未安装”,六项检查全部失败,警告不再出现。
为什么开启 KernelSU 的“卸载模块”功能对 WPS 的 Root 检测无效?
因为 WPS 的检测并不依赖 su 文件或挂载点,而是通过包名查询判断 KernelSU 管理器是否安装。卸载模块只影响文件层面的隐藏,无法阻止包可见性查询,所以对这项检测无效。
作者写的 LSPosed 模块 wps-root-toast 有什么作用?
该模块通过方法级 Hook 精确匹配 WPS 的 Root 警告 Toast 文本,仅跳过该 Toast 的显示,放行其他正常 Toast,不改变 WPS 的检测结果。它只能隐藏主页的警告,无法阻止检测本身。
WPS 的 Root 检测结果会上传到服务器吗?
文章指出,代码中还有两处独立检查:LogoutTracker.h() 和 GetDeviceInfoHandler.a(),后者会将 Root 检测结果放入设备信息的 root 字段并通过 JS 回调返回。但仅从代码无法确定是否上传到服务器,作者未下结论。