你关掉开关,它却打开列表:WPS 如何知道我的手机已 Root
内容提要
作者发现WPS每次启动都提示设备已Root,即使已拒绝其读取应用列表权限。分析WPS 26.9.1代码后确认,检测通过六项检查,实际触发点是查询KernelSU包名,而Android 11的包可见性机制使该查询无需权限。作者先写LSPosed模块屏蔽提示,后改用Hide My Applist隐藏包名,从源头使检测返回未Root。
延伸解读
权限开关为何形同虚设
文章指出,用户拒绝WPS的“读取应用列表”权限后,WPS仍能检测到KernelSU。这是因为Android 11的包可见性机制允许应用在Manifest中通过<queries>声明特定包名,查询这些包是否安装无需任何运行时权限。WPS的<queries>中列出了KernelSU、Magisk等,因此权限面板中的拒绝操作对这类定向查询无效,给用户的控制感是一种错觉。
Root检测的实际触发点
通过分析WPS 26.9.1代码,作者发现首页的Root判定由KSystemRoot.i(context)方法执行,依次进行六项检查:su文件、系统属性、ro.secure、Build.TAGS、模拟器特征以及Root管理器包名查询。诊断构建显示,前五项均未命中,实际触发点是查询me.weishu.kernelsu包名。这解释了为何之前针对su文件和挂载的隐藏手段均无效。
从隐藏提示到源头屏蔽
作者最初编写LSPosed模块仅屏蔽特定Toast,但检测仍在运行。后来改用Hide My Applist(HMA)隐藏KernelSU包名,使包查询返回“未安装”,从而让六项检查全部落空。与仅隐藏提示相比,源头屏蔽能同时消除其他入口的相同检测,但HMA需要框架支持system_server内加载原生库,且作者仅在Android 16 + LSPosed 2.2.1上测试通过。
诊断与症状缓解的先后顺序
作者反思,虽然最终发现现有工具HMA已足够,但前期编写模块并非徒劳。若不先逆向分析出六项检查链,就无法定位触发点是包名查询,也不会想到用HMA模板解决。诊断构建本身也源自该模块代码,没有它就无法找到根因。因此,先诊断再选择缓解手段,比直接尝试屏蔽症状更有效。
Q&A
WPS 每次启动都提示设备已 Root,这个警告为什么无法关闭?
该警告是应用进程直接弹出的 Toast,不经过系统通知系统,因此通知权限和历史记录都不适用,WPS 自身设置也没有开关。即使用户拒绝了 WPS 的“读取应用列表”权限,它仍能检测到 KernelSU。
WPS 是如何判断手机是否已 Root 的?
WPS 26.9.1 的首页判断收敛于 KSystemRoot.i(context) 方法,依次执行六项检查:PATH 目录下是否存在 su 文件、persist.sys.root.status 属性是否非 0、ro.secure 是否为 0、Build.TAGS 是否包含 test-keys、指纹或型号是否显示模拟器特征、是否找到 Root 管理器包名。任何一项命中即返回 true。
为什么我拒绝了 WPS 的“读取应用列表”权限,它还是能检测到 KernelSU?
因为“读取应用列表”权限控制的是枚举设备上所有应用,而 Android 11 的包可见性机制允许应用在 Manifest 的 <queries> 中声明特定包名,对这些指定包的安装状态查询无需运行时权限。WPS 的 <queries> 中列出了 KernelSU、Magisk 和 APatch,因此即使权限被拒,它仍能查询到这些包。
如何让 WPS 的 Root 检测返回“未 Root”?
可以使用 Hide My Applist (HMA) 模块,配置一个只包含 me.weishu.kernelsu 的黑名单模板,并将该模板应用到 WPS。这样包查询会被拦截,六项检查链返回 false,从而从源头消除检测,而不仅仅是隐藏提示。
作者写的 LSPosed 模块 wps-root-toast 有什么作用?
该模块通过精确匹配警告语句,仅拦截并跳过该 Toast 的显示,不影响其他 Toast,也不改变 WPS 的 Root 检测结果。它适用于只想消除警告而不修改包可见性的用户,并且其诊断版本帮助定位了检测触发点。
WPS 的 Root 检测链存在哪些不合理之处?
检测链仅凭安装了管理器应用就判定为 Root,而不检查 WPS 是否真的能获取 Root 权限;同时将模拟器特征也视为 Root,可能对模拟器用户造成误判。这些判断与“安全风险”的表述并不完全匹配。