ARTICLE · 1182005
只申请 6 个权限的 App,怎么读你整个屏幕?
昨晚拆了一个 25,781,877 字节的 APK。
App 叫「Jev助手」,前段时间小火过一阵,号称能"读懂你们的聊天,帮你生成回复建议"。它的权限清单我数了三遍,一共 6 条:联网、悬浮窗、前台服务、发通知、查网络状态,再加一个声明给系统的无障碍服务。
没有相机。没有存储。没有通讯录。没有麦克风。
但它能读你微信屏幕上的每一个字,能静默截图,还能往你的输入框里写字。
我盯上它,用了三层验证
第一疑问:它是真在判断,还是大模型加预制 prompt 的套路?聊天回复提前写好模板照样能演。
第一个佐证来自 OpenRouter 的账单。

图1:OpenRouter 账单,9 月 23 日的 Jev 1.13 调用
9 月 23 日上午,账单里冒出条 "Jev 1.13" 的调用。那会平台有活动,费用 0.00 美元,但 API 真被调用了。我第一反应:这东西真在工作,而且自称 "Jev"。
离谱。
第二个佐证是我自己用了一次。一段离职证明对话,它的输出:对方真实意图"要你办事",把握 70%,危险 0/9 安全,候选回复第一条置信度 87%,右下角一个"填入"按钮。

图2:Jev 实测截图:判断结果与候选回复
预制 prompt 能演聊天,演不出这个:每段对话的判断、把握度、排序都不一样,它还把中间结果明明白白摊给你看。
但产品界面自己说的话,一条都不能全信。第三步才是拆:拿它官方页面的"工作原理"自述,回到代码里逐条对账。
它怎么工作的:一条七重闸门的采集链
我照代码逐行数过,它每读一屏,要过七道关:
①
判据:用户总开关开着
不满足时:收悬浮球,返回
②
判据:前台 App 在适配器名单里
不满足时:显示待机,返回
③
判据:拿得到窗口树
不满足时:返回
④
判据:认出了聊天结构
不满足时:返回
⑤
判据:标题在白名单里
不满足时:返回
⑥
判据:消息非空
不满足时:转 OCR 兜底
⑦
判据:内容和上次不一样
不满足时:返回
真正决定"要不要读这一屏"的是第⑤关,它的代码是我整份分析里最该记住的一段:
public final boolean isAllowed(String title) {
Set<String> whitelist = getWhitelist();
if (whitelist.isEmpty()) return true;
if (title == null) return false;
for (String w : whitelist)
if (title.contains(w)) return true;
return false;
}
这段代码有四个值得停下来看一眼的地方:白名单空的时候允许全部,"没配置"这个状态等于最大权限;标题为空又拒绝,同一个函数里混了两种失败策略;匹配用的是 contains 不是 equals,填「张」命中所有带"张"的会话,填空串命中一切;匹配的是窗口标题,切会话瞬间,标题可能是上一个联系人的。
这里有个通用方法论,读任何校验逻辑都用得上:配置缺失、数据异常时,这段代码默认走向允许,还是拒绝?
顺便对个位。图2 里每个元素,代码里都有名字:对方真实意图 true_intent,危险 0/9 是 danger_level,紧张已缓解是 tension_resolved,"填入"是 ACTION_SET_TEXT,直接写进输入框。本地知识库有开关,默认没开。
回到标题:6 个权限,凭什么读你整个屏幕?
答案在 manifest 里。
这个 App 申请的无障碍服务,组件类名写的是 G社官方"Select to Speak"的类名,配置文件直接命名 config_disguised,"伪装"的意思。它从系统拿到的能力全在配置里:typeAllMask 订阅所有界面事件,canRetrieveWindowContent 读当前窗口整棵控件树,canTakeScreenshot 静默截图,flagRetrieveInteractiveWindows 连其它窗口含悬浮层一起取,flagReportViewIds 拿到控件的 view id 字符串,flagIncludeNotImportantViews 把"不重要"的控件也给你。
它适配了四个 App:微信、QQ、海外社交平台、飞书。微信那个硬编码的 view id 本来能生效,靠的就是 flagReportViewIds,配置和代码互相咬合。
稿子写到这儿,我顺手又试了一次:微信用不了了。悬浮球浮不起来,识别也停了,只剩 QQ、飞书、X 和 WhatsApp 四个地方能跑。
是官方撤的,还是微信改版改没的,我没证据,不下结论。但这件事本身就是前面所有分析的实证:它能在哪干活,从来不取决于系统给了几条权限,而取决于它自己那张名单。名字在名单里,6 个权限就能组合出读屏的能力;名字不在,能力当场归零,权限一条都没少。
一个不要存储权限的 App,数据在哪?在屏幕上。无障碍不用碰你的文件,直接读屏幕。
动手比想的简单
第一步我连工具都没装。APK 就是 ZIP,一条 unzip -l 看完骨架:977 个文件,两个 DEX 加起来 11.18 MB,一个 11 MB 的离线 OCR 模型,签名只有 v2/v3,没有 v1 JAR 签名。
然后是关键词搜索。这一层差点让我得出完全相反的结论。
我接到的第一个要求,是搜 password、license、verify、VIP,找所谓的"核心校验逻辑"。按包名分栏统计之后,结果是这样:
password
应用自有命中:1
全 APK:19
应用侧命中的真实含义:一个参数名,把 API Key 输入框显示成圆点
license / verify / VIP
应用自有命中:0
全 APK:2 / 16 / 1
应用侧命中的真实含义:全在库代码
root
应用自有命中:17
全 APK:125
应用侧命中的真实含义:控件树的根节点
active
应用自有命中:68
全 APK:577
应用侧命中的真实含义:Activity 这个词
三类经典假阳性全凑齐了。不按包名分栏、不看上下文,这篇标题就得改成"该应用具备 root 检测与反调试能力"。
反编译出来,应用自己的代码 207 个类,库代码 10,649 个,51 比 1。信噪比:你搜出来的每 52 条里,51 条是噪音。
踩坑记录里真正值得留下的,是这类"结论悄悄变错"比"命令报错"危险得多的经验。
踩坑记录
1
现象:下载 JRE 连续 403
根因:镜像站放行 curl 的 UA,拒绝浏览器 UA,跟常规反爬直觉相反
解法:换成 curl 的 UA,立刻通
2
现象:GitHub 大文件下载超时
根因:API 层通,大文件对象层不通,两个是不同 CDN
解法:元数据走 API,大文件走镜像,用官方字节数验收
3
现象:jadx 报 87 个错误
根因:Kotlin 协程类型信息编译时已丢失,工具默认跳过还原不全的类
解法:加 --show-bad-code;目标从"0 错误"改成"0 盲点"
4
现象:关键词搜出来全是乱命
根因:库代码是业务代码的 51 倍,且一词多义
解法:搜索前先按包名收窄;命中后看词形
5
现象:42 个 java 对 207 个 smali
根因:一类一文件和折叠回宿主的粒度差异
解法:按宿主类对账,不按文件数
6
现象:引用行号错了 6 处
根因:grep -n 放在过滤管道后面,数的是过滤后流的行号,不是文件行号
解法:行号必须在原始文件上取;写个断言器逐条核"第 N 行是否含该内容",61 条全绿才进文档
第 6 条多说一句:它不报错、不崩溃,只安静给出偏移过的行号,你按行号去看,会看到完全无关的内容。这类"静默错位"只能靠机械校验兜底,细心兜不住。
跟官方说明逐条对账
官方页面把原理写成三步:读取,先做出判断,草拟、检查、排序,"然后,你点击发送"。

图4:官方 README 的工作原理
我把每条声明钉到代码上,四条结论:
读取:辅助功能、设备端 OCR、系统文本 API 截屏
拆出来的事实:成立。但截图走无障碍通道,不弹"允许屏幕录制"那个确认框
先做出判断:真实意图、风险程度、应对方式
拆出来的事实:成立,而且比页面多做 4 个判断,其中一个页面没提:现在该不该回
然后,你点击发送
拆出来的事实:成立,穷举验证。全部动作调用点只有 3 个,写得到输入框,按不到发送键
您在 iOS 上复制的消息
拆出来的事实:本 APK 里没有对应实现,阴性结论
它不会修改聊天应用
拆出来的事实:措辞要修正。它会往你的输入框写字,包括先清空再粘贴
官方没有撒谎,每条声明单独看都站得住。但组合起来的边界,比读起来模糊。
同一条链路也有桌面端形态:读取、判断、填入,搬到 PC 的聊天窗口上一样跑。

图3:官方演示的桌面端形态
三条能直接带走的经验
第一,能力组合大于单条权限。联网、悬浮窗、无障碍、常驻前台,各自都有大量正当用途,合起来才是判据。清单很短也不代表无害:它没要存储相机通讯录,因为数据在屏幕上。
第二,minSdk 是权限策略,不只是兼容性选择。静默截图是 API 30 以上的能力,这个 App 的 minSdk 正好卡在 30。版本下限落在哪,是设计出来的。
第三,读校验代码只问一个问题:缺省值朝哪边倒。"有没有校验"不是重点,"校验失效时是否默认放行"才是。这个 App 白名单为空返回 true,标题为空又返回 false。
白名单为空的时候,代码选择允许全部会话。你觉得这是疏忽,还是设计?
成本、工具和门槛
整场分析用到的东西:一个 Java 运行时、apktool、jadx,全装在工程目录里,不碰系统盘。rea 做交叉验证,每个关键坐标我都自己裸读、重算哈希复核过。不需要真机和 root,一个下午够了。
边界和下一步
全程只读。没改一行代码,没重新打包,没安装。
这个"没改过"不是嘴说,是可复跑的验证:我重新解包一次,跟已产出目录逐文件比对,差异 0 行,manifest 逐字节一致。
以下问题这次回答不了,也不猜:运行时是否真按静态代码走,签名者是谁,有没有真实用户,OCR 兜底真实触发多少。
从 9 月 23 日账单上那条 "Jev 1.13",到昨晚拆完最后一段代码,这个问题我盯了三个星期:它到底是不是真在判断。现在答案是,真在判断。出乎意料的不是这个,是它比官方页面更能看:读整棵控件树,静默截图,往你的输入框里写字;又比官方页面更克制:全部动作调用点只有 3 个,按不到你的发送键。
这就是拆解的意思:不是猜,是拆开看。
你手机里装没装过这类"助手"?我手机里现在就装着一个,从 9 月下旬装到昨晚拆完。看看权限清单,再看一眼无障碍服务配置,很多事不用拆代码就能看明白。
评论区聊聊:你现在手机里"权限最小但最瘆人"的 App 是哪个?















































































