先分清是“提示”还是“拦截”
Play Protect 的提示通常出现在安装或扫描阶段,表现形式并不完全一样:有的只是安装时弹出“可能有害”的提醒,用户仍可继续;有的会直接阻断安装或建议卸载。排查前先记录三件事:提示出现的时机(安装中/首次启动/后台扫描)、提示文案里的关键词、以及出问题的包是来自应用市场、官网下载还是侧载。这三项决定了后面从哪里入手。
需要说明的是,Play Protect 的判定逻辑并未完全公开,我们能做的是排除已知的高风险特征,而不是去“猜规则”。下面这条顺序按排查成本从低到高排列,建议逐项走完再决定是否加固或重新出包。
第一步:权限与敏感 API 调用
权限是最容易被判为高风险的维度,尤其是与权限实际用途不匹配的组合。
- 对照 manifest 列出全部危险权限,逐个回答“这个功能在哪个页面用到”。用不到的立即移除,包括第三方 SDK 自动合并进来的权限。
- 重点看短信、通话记录、通讯录、无障碍服务、悬浮窗、安装未知应用、读取已安装应用列表这几类。它们单独出现未必有问题,但和“后台常驻”“自启动”叠加时风险权重会明显上升。
- 检查是否存在启动即申请、用户拒绝后反复弹窗的行为。这类体验问题在自动化扫描中也可能被标记。
- 核对 targetSdkVersion。版本过低会缺失新系统的权限约束,容易被归入“老旧高风险应用”。
这一步的产出应该是一张权限清单表:权限名、使用场景、是否可移除、移除后的影响。后续整改都围绕这张表进行。
第二步:第三方 SDK 与广告组件
很多“APK 报毒”的根因不在自研代码,而在合并进来的 SDK。排查时不要只看 build.gradle 里写了什么,要看最终产物。
- 用反编译或包分析工具打开最终 APK,列出所有包名与 SDK 版本,和构建配置逐一对账,找出“没主动引入却存在”的组件。
- 关注广告聚合、统计埋点、热更新、动态加载类 SDK。动态下发代码、静默下载、后台拉活是高风险特征。
- 检查 SDK 是否在后台频繁请求定位、读取设备标识,或存在难以关闭的常驻服务。
- 对不再维护或来源不明的 SDK 直接下线,不要指望靠混淆掩盖。
如果团队同时维护多个渠道包,建议把 SDK 清单纳入发版检查项,避免某个渠道包单独引入高风险组件而拖累整体。
第三步:网络传输与下载行为
网络层的问题往往最隐蔽,但排查起来并不复杂。
- 确认所有接口走 HTTPS,并检查是否存在明文 HTTP 兜底地址、写死的测试域名或 IP 直连。
- 检查是否配置了过于宽松的网络安全策略,例如全局允许明文流量、信任用户安装的证书。
- 排查应用内是否有“下载 APK 并引导安装”的逻辑。这类行为即使合规,也极易触发风险提示,需评估是否必要。
- 看是否存在启动时向未知域名上报设备信息的行为,尤其是自研代码之外的调用。
抓包是最直接的手段:在干净设备上完整走一遍主要流程,记录所有外联域名,逐个确认归属。发现不属于自家业务的域名,回到第二步定位是哪个 SDK 发出的。
第四步:签名完整性与包体一致性
签名问题常被忽略,但它直接影响“这个包是不是被篡改过”的判断。
- 核对签名证书指纹是否与历史版本一致。换过签名、用过不同 keystore 出包,都会让新包看起来像“另一个应用”。
- 检查是否启用了 V2/V3 签名方案,仅用 V1 的老包在新系统上更容易被质疑。
- 确认包体没有被二次打包:对比官方渠道包与用户实际安装包的签名、文件哈希、入口 Activity。
- 检查是否存在加固或多层壳导致的异常结构。加固本身不是问题,但过度加壳、隐藏入口可能增加误判概率。
如果这一步发现签名不一致,优先解决分发链路问题,而不是继续在代码层找原因。关于签名与安装失败的排查思路,可以参考站内 相关服务说明 中的整理。
整改后的复测方法
改完不要直接全量发布。建议按以下顺序验证:
- 用干净设备(未登录账号、未安装同类应用)安装新包,观察安装阶段是否仍有提示。
- 完整走一遍核心流程,包括首次启动、权限申请、后台切换、下载类操作。
- 保留旧包作为对照,确认提示消失是整改带来的,而不是环境差异。
- 小范围灰度后再放量,观察用户侧反馈是否出现新的异常。
需要提醒的是,扫描结果存在一定波动,单次复测通过不代表永久安全。把权限清单、SDK 清单、外联域名清单固化为发版前的例行检查,比事后补救更省成本。团队协作与流程上的建议,可以在 关于我们 页面了解我们的处理方式。
常见误区
- 一提示就加固:加固解决的是逆向与篡改问题,对权限滥用、SDK 拉活基本无效。
- 只看自研代码:最终产物里的合并组件才是重点。
- 忽略分发链路:用户装到的包可能不是你们出的包,先确认来源再谈整改。
按“权限 → SDK → 网络 → 签名”的顺序走,多数提示都能定位到具体原因,剩下的再交给安全加固与合规评估。