Google Play 上大多数卡住发版的问题,其实都能直接从你准备提交的那个文件里读出来。targetSdkVersion 写在 AndroidManifest.xml 里;原生库是否满足 16 KB 对齐,写在 ELF 程序头里;是不是用调试密钥签的名,写在证书里。这些都不需要服务器,也不需要源码。
这个工具在浏览器里直接读取打包好的 APK 或 AAB 中的这些字段,对照 Google Play 上架和线下分发中最常导致发版受阻的规则逐项检查。每条结果都附上判断依据(证据)、修复方法,以及写明该规则的官方页面链接。
使用步骤
- 准备好要发布的
.apk或.aab(Gradle 输出在app/build/outputs/下),最大 1 GiB。 - 先选分发渠道(Google Play、Play 以外(线下分发)或两者都要),体检日期保持今天,再把文件拖到拖放区,或点“选择文件”。
- 先看各级别的数量,再逐条看“拒审”和“高风险”的证据与修复方法;下面的 Manifest、原生库、代码(DEX)和签名几节是原始数据。
真实输出示例
我们从 F-Droid 下载 Termux:Float 0.17.0,选“两者都要”、日期 2026-11-02,复制了中文页面显示的报告。排版有压缩(标签和值放在一行,省略“触发条件”和官方依据,签名者 2、3 与签名者 1 相同故删去),所有数值原样保留。
样本: com.termux.window_1001.apk(Termux:Float 0.17.0,GPL-3.0-only),2026-11-02 从 F-Droid 下载,SHA-256 380f0dbb…8e220 (f-droid.org/packages/com.termux.window/)
摘要
包名 com.termux.window
应用名称 Termux:Float
版本 0.17.0 (1001)
minSdk 24
targetSdk 28
类型 APK · 1.9 MB
SHA-256(整个文件) 380f0dbb35d3142931b8918b976bac05ff59a717e8b396b9c82f72b894b8e220
拒审 1 · 高风险 3 · 建议 3 · 未知 0 · 通过 39
拒审 (1)
PLAY-01 targetSdkVersion 未达到 Google Play 要求 拒审
证据 targetSdkVersion=28
required: >= 36 (phone/tablet, since 2026-08-31)
高风险 (3)
PLAY-02 16 KB 页面大小:ELF 段未按 16 KB 对齐 高风险
将于 2027-02-01 起生效(届时为拒审),此前按高风险提示。
证据 lib/arm64-v8a/libtermux.so: PT_LOAD p_align=0x1000 (< 0x4000)
lib/x86_64/libtermux.so: PT_LOAD p_align=0x1000 (< 0x4000)
修复方法 升级到 NDK r28 或以上(默认 16 KB 对齐)。NDK r27 及以下加链接参数 -Wl,-z,max-page-size=16384。…
PLAY-05 提交 Google Play 用的是 APK 而不是 App Bundle 高风险
证据 input is an APK
SIDE-02 targetSdk 过旧:安装时 Play Protect 警告 高风险
证据 targetSdkVersion=28
latest Android API level on 2026-11-02: 37
建议 (3)
SIDE-10 Android 开发者验证:需要登记的信息 建议
证据 package: com.termux.window
signer SHA-256: 228fb2cfe90831c1499ec3ccaf61e96e8e1ce70766b9474672ce427334d41c42
PROT-03 Play Integrity API 建议
PROT-04 运行时动态加载 dex 或 so 建议
证据 dalvik.system.PathClassLoader.<init> called from org.lsposed.hiddenapibypass.CoreOjClassLoader
…
原生库
ABI 名称 大小 存储方式 ELF 最大 p_align 16 KB 对齐
arm64-v8a libtermux.so 9.8 KB 压缩 64-bit AArch64 0x1000 否
armeabi-v7a libtermux.so 17.6 KB 压缩 32-bit ARM 0x1000 否
x86 libtermux.so 9.4 KB 压缩 32-bit x86 0x1000 否
x86_64 libtermux.so 10.0 KB 压缩 64-bit x86-64 0x1000 否
代码(DEX)
文件 版本 大小 类 方法引用 字符串 校验和 / SHA-1
classes.dex 037 1.8 MB 1687 13998 16182 一致 一致
classes2.dex 037 844 B 1 2 9 一致 一致
~~R8{"backend":"dex","compilation-mode":"release",…,"min-api":24,"pg-map-id":"22b2a8e","r8-mode":"full","version":"8.7.18"}
…
签名
签名方案 v1, v2, v3
签名者 1 · v1
主题 CN=FDroid,OU=FDroid,O=fdroid.org,L=ORG,ST=ORG,C=UK
有效期 2015-10-26T07:35:59Z → 2043-03-13T07:35:59Z
密钥 RSA 2048
SHA-256 228fb2cfe90831c1499ec3ccaf61e96e8e1ce70766b9474672ce427334d41c42
SHA-1 e8754a2c614392071d27021bbcdfb8dda7e0711c
…能检查什么
目前共有 47 条检查,覆盖 Google Play 上架要求、线下分发与安装问题、签名、隐私和应用防护。规则编号与本站使用的规则清单一致,报告里的 #PLAY-02 这类锚点始终指同一条规则。“触发条件”一列写明了具体条件,例如 SIGN-03 只有在 targetSdk ≥ 30 时才按拒审处理。如果你只关心 Google Play,可以用 Google Play 上架合规体检:同一个工具,默认选中 Play 场景。
| 编号 | 检查内容 | 级别 | 触发条件 | 适用场景 |
|---|---|---|---|---|
PLAY-01 | targetSdkVersion 未达到 Google Play 要求 | 拒审 | 仅 Play。手机/平板:2026-08-31 起 targetSdk ≥ 36(可延期到 2026-11-01 期间,≥ 35 的按高风险);Wear OS/Automotive ≥ 35;TV ≥ 34;XR ≥ 34。设备形态取自 required 的 <uses-feature>。 | Google Play |
PLAY-02 | 16 KB 页面大小:ELF 段未按 16 KB 对齐 | 拒审(2027-02-01 起) | 仅 Play。lib/arm64-v8a 和 lib/x86_64 下任一 PT_LOAD 的 p_align < 0x4000。2027-02-01 前为高风险,之后为拒审。 | Google Play |
PLAY-03 | 16 KB 页面大小:未压缩的 .so 在 ZIP 内未按 16 KB 对齐 | 拒审(2027-02-01 起) | 仅 Play,仅 APK。不压缩(STORED)的 64 位 .so 数据偏移不是 16384 的倍数;extractNativeLibs="true" 时跳过。2027-02-01 前为高风险。 | Google Play |
PLAY-04 | 缺少 64 位原生库 | 拒审 | 仅 Play。有 armeabi-v7a/armeabi 却没有 arm64-v8a,或有 x86 却没有 x86_64;或 ELF 的位数/机器类型与所在 ABI 目录不符。 | Google Play |
PLAY-06 | 可调试包或测试包 | 拒审 | 仅 Play。application 上 android:debuggable="true" 或 android:testOnly="true"。 | Google Play |
PLAY-07 | 包名使用了保留前缀 | 拒审 | 仅 Play。包名为 com.example.*、com.android.* 或 android.*。 | Google Play |
PLAY-08 | 短信或通话记录权限 | 拒审 | 仅 Play。申请了任一短信或通话记录权限。 | Google Play |
PLAY-09 | QUERY_ALL_PACKAGES 权限 | 高风险 | 仅 Play。申请了 QUERY_ALL_PACKAGES(高风险:需声明审核)。 | Google Play |
PLAY-10 | 所有文件访问权限 | 拒审 | 仅 Play。MANAGE_EXTERNAL_STORAGE 为拒审;只有 requestLegacyExternalStorage="true" 为高风险。 | Google Play |
PLAY-13 | 后台位置权限 | 高风险 | 仅 Play。申请了 ACCESS_BACKGROUND_LOCATION。 | Google Play |
PLAY-16 | 照片和视频权限 | 拒审 | 仅 Play,且仅当 targetSdk ≥ 33:申请了 READ_MEDIA_IMAGES 或 READ_MEDIA_VIDEO。 | Google Play |
SIDE-01 | targetSdkVersion 过低,新版 Android 无法安装 | 拒审 | Play 以外。targetSdk < 24(Android 15 及以上拒绝安装)或 < 23(Android 14 及以上)。 | Play 以外(线下分发) |
SIGN-01 | 使用了 Android 调试证书签名 | 拒审 | 任一签名者证书主题的 CN 为 "Android Debug"(SDK 调试密钥是 CN=Android Debug, O=Android, C=US;React Native 等框架模板的 O 值不同)。Play 为拒审,Play 以外为高风险。Google 写明 Play 不接受调试证书签名的应用;Play 很可能就是按这个主题识别的,但官方没有写明。 | Google Play / Play 以外(线下分发) |
SIGN-03 | 只有 v1(JAR)签名 | 拒审 | 仅 APK。没有 v2/v3 签名:只有 targetSdk ≥ 30 时为拒审(无法安装),低于 30 为高风险;完全没有签名单独提示。 | Google Play / Play 以外(线下分发) |
PLAY-05 | 提交 Google Play 用的是 APK 而不是 App Bundle | 高风险 | 仅 Play 场景,仅 APK。高风险:新应用必须以 AAB 发布(2021 年 8 月起),Google 页面写明的是对新应用的要求。Android TV 应用(required 的 android.software.leanback)为拒审:2023-06-30 起 Play 不再接受 TV 应用用 APK 更新。 | Google Play |
PLAY-11 | 声明了无障碍服务 | 高风险 | 仅 Play。任一受 BIND_ACCESSIBILITY_SERVICE 保护的 <service>。资源表能解析时,同时读取服务配置 XML 中的 android:isAccessibilityTool。 | Google Play |
PLAY-14 | 精确闹钟权限 | 高风险 | 仅 Play。USE_EXACT_ALARM 为高风险(只允许核心功能是闹钟或日历的应用)。只有 SCHEDULE_EXACT_ALARM 为建议(用户可以拒绝授权)。 | Google Play |
PLAY-15 | 前台服务类型与全屏通知 | 高风险 | 仅当 targetSdk ≥ 34。拒审(所有场景):service 声明了 foregroundServiceType 却缺少对应的 FOREGROUND_SERVICE_<TYPE> 权限(启动即崩溃)。高风险:有 FOREGROUND_SERVICE 却没有任何声明类型的 service;Play 场景下另含每个带类型的前台服务(需填 Play Console 声明)以及 USE_FULL_SCREEN_INTENT。哪些未声明类型的 service 会以前台方式启动不做追踪(需要分析代码中的数据流)。 | Google Play / Play 以外(线下分发) |
PLAY-17 | 通讯录权限(Play 新规) | 建议 | 仅 Play。申请了 READ_CONTACTS:建议;targetSdk ≥ 37 时为高风险(最小范围新规适用于以 Android 17 为目标的应用,2027-01-27 起执行)。 | Google Play |
SIDE-02 | targetSdk 过旧:安装时 Play Protect 警告 | 高风险 | Play 以外。targetSdk 比体检日期当时最新的 Android 版本低两个以上 API 级别(最新级别由规则数据维护)。 | Play 以外(线下分发) |
SIDE-03 | 增强型反诈保护会拦截的权限 | 高风险 | Play 以外。RECEIVE_SMS、READ_SMS、通知监听或无障碍服务。在已启用 Google Play Protect 增强型反诈保护的部分市场(Google 未公布完整名单),从浏览器、聊天软件或文件管理器安装这类应用会被拦截。 | Play 以外(线下分发) |
SIDE-04 | 无障碍服务标记为辅助功能工具 | 高风险 | Play 以外。无障碍服务的配置里 isAccessibilityTool="true"。只是提示:应用是否真的是面向残障用户的工具,需要人工确认。配置 XML 读不到时为未知。 | Play 以外(线下分发) |
SIDE-06 | 高危能力组合 | 高风险 | Play 以外。同时具备以下三项或以上:SYSTEM_ALERT_WINDOW、无障碍服务、短信权限、设备管理员 receiver、通知监听。 | Play 以外(线下分发) |
SIDE-07 | 没有启动入口 | 建议 | Play 以外。没有带 MAIN + LAUNCHER(或 LEANBACK_LAUNCHER)的 activity/activity-alias,也没有输入法、壁纸、无障碍、屏保、TV 输入、桌面等系统类组件。 | Play 以外(线下分发) |
SIDE-08 | 拆分 APK 被当作完整应用分发 | 拒审 | Play 以外,仅 APK。拒审(无法单独安装):文件本身是拆分包(<manifest split>、isFeatureSplit),会报 INSTALL_FAILED_INVALID_APK "Full install must include a base package";或 base APK 的 <manifest> 上设置了 android:isSplitRequired="true" 或 android:requiredSplitTypes,会报 INSTALL_FAILED_MISSING_SPLIT。高风险:base APK 只在 <application> 上带 android:isSplitRequired,或带 meta-data com.android.vending.splits.required。安装器不读这两处,所以可能装得上,但缺了拆分包就不完整(拆分包里的代码或资源运行时可能找不到)。 | Play 以外(线下分发) |
SIDE-09 | ZIP 结构不规范 | 高风险 | 仅 APK。重复条目、中央目录与本地头不一致、不安全路径、加密标志位或未知压缩方式为高风险;归档末尾有多余数据为建议。 | Google Play / Play 以外(线下分发) |
SIDE-10 | Android 开发者验证:需要登记的信息 | 建议 | Play 以外,仅 APK。始终列出需要登记的包名和签名证书 SHA-256。签名者是调试证书或公开的 AOSP 测试密钥时为高风险,因为无法证明所有权。 | Play 以外(线下分发) |
SIGN-02 | 使用了公开的密钥签名 | 拒审 | 任一签名者证书的 SHA-256 与私钥已公开的密钥一致:AOSP 测试证书(testkey、platform、shared、media 等),或 React Native 模板的 debug.keystore(Expo bare 模板里是同一个文件)。Play 为拒审,Play 以外为高风险。 | Google Play / Play 以外(线下分发) |
SIGN-04 | 签名密钥或证书摘要强度不足 | 高风险 | RSA 密钥小于 2048 位、DSA 密钥,或证书签名基于 MD5 为高风险;证书签名基于 SHA-1 为建议。 | Google Play / Play 以外(线下分发) |
SIGN-05 | 签名证书在 2033-10-22 之前到期 | 拒审 | 仅 Play。任一签名者证书的 notAfter 早于 2033-10-22。APK:拒审(该规定针对应用签名密钥)。AAB:只是建议(AAB 上的是上传密钥,Google 用应用签名密钥签名应用,2033 年的规定针对的是后者)。 | Google Play |
SIGN-08 | ZIP 对齐与 resources.arsc 存储方式 | 拒审 | 仅 APK。只有 targetSdk ≥ 30 且 resources.arsc 被压缩或未按 4 字节对齐时为拒审(无法安装)。其他未压缩条目未按 4 字节对齐为建议。 | Google Play / Play 以外(线下分发) |
PRIV-07 | 允许明文传输或信任用户安装的证书 | 建议 | usesCleartextTraffic="true";或未声明且 targetSdk < 28(此时默认允许明文);或网络安全配置在 <debug-overrides> 之外允许明文或信任用户证书。配置读不到且没有其他发现时为未知。 | Google Play / Play 以外(线下分发) |
PROT-01 | 识别到应用防护(加壳 / 加固) | 建议 | 只做识别:按库文件名识别到商业应用防护运行库,或者(弱信号)只有一个很小的 classes.dex 而 assets 中有大文件。保护代码是正当的,这里只说明利弊。 | Google Play / Play 以外(线下分发) |
PLAY-12 | REQUEST_INSTALL_PACKAGES 权限 | 高风险 | 仅 Play。申请了 REQUEST_INSTALL_PACKAGES:高风险(只有安装其他应用属于核心功能时才允许,例如文件管理、浏览器、企业设备管理)。DEX 同时显示自更新链路时为拒审:有安装路径(PackageInstaller 调用或安装 intent)且有 .apk 下载地址字符串——Play 上的应用不得通过 Google Play 以外的方式更新自身。DEX 读不到时只按权限提示。 | Google Play |
SIDE-05 | 自更新链路 | 高风险 | Play 以外。三项同时存在为高风险:REQUEST_INSTALL_PACKAGES 权限、DEX 中的安装路径(PackageInstaller 调用,或安装 intent 的 MIME 类型 / action)、.apk 下载地址字符串。有权限和安装路径但没有地址字符串为建议(地址常在运行时拼接)。DEX 扫描不完整且没找到时为未知。 | Play 以外(线下分发) |
SIGN-06 | v1(JAR)签名与内容不一致 | 拒审 | 存在 v1 / JAR 签名时:逐项比对 MANIFEST.MF 摘要与实际条目,列出 META-INF/ 以外未被清单覆盖的条目,并校验 .SF 中对 MANIFEST.MF 的摘要(不验证 .SF 上的 PKCS#7 签名)。APK:只有 v1 签名或 minSdk < 24 时为拒审(Android 7.0 及以上改为校验 v2/v3,忽略 v1),否则为建议。AAB(用上传密钥做 JAR 签名):高风险,Google 没有写明上传签名损坏时如何处理。 | Google Play / Play 以外(线下分发) |
SIGN-07 | v2 / v3 签名校验失败 | 拒审 | 仅 APK。重新计算每个 v2 / v3 / v3.1 签名者按 1 MiB 分块的内容摘要,并用 WebCrypto 验证签名。摘要不一致或签名验证失败为拒审(Android 无法安装)。WebCrypto 不支持的算法(DSA)或校验触及上限时为未知。v3.2(Android 17 混合 ML-DSA)签名块不校验。 | Google Play / Play 以外(线下分发) |
SIGN-09 | 与上一版本的签名不一致 | 拒审 | 仅在通过"与上一版本比较"加入上一版本后检查。比较两个文件签名者证书的 SHA-256;v3 / v3.1 的轮换谱系(proof-of-rotation)包含上一版全部签名者时视为一致(不验证谱系本身的签名)。APK 对 APK:拒审(更新装不上旧版本)。涉及 AAB 时为高风险:AAB 上是上传密钥,用户拿到的 APK 由 Play App Signing 重新签名,换上传密钥需要在 Play Console 申请重置。 | Google Play / Play 以外(线下分发) |
PRIV-01 | 需要在 Google Play SDK Index 核对的第三方 SDK | 建议 | 列出识别到的全部 SDK(Manifest 组件和 meta-data、原生库、版本文件、DEX 类名前缀和版本常量)。建议级:SDK Index 没有公开 API,离线无法判断版本是否过时或被标记违规,请逐个查询。 | Google Play / Play 以外(线下分发) |
PRIV-02 | Data safety 表单:核对草稿 | 建议 | 仅 Play。根据权限和识别到的 SDK 类别,列出 Data safety 表单大概率需要申报的数据类型。工具看不到你在 Play Console 的填写,这只是用来对照的草稿,不是结论。 | Google Play |
PRIV-03 | 广告 ID 权限与声明 | 高风险 | 仅 Play。高风险:有广告或统计 SDK、targetSdk ≥ 33 却没有声明 AD_ID(此时 getId() 返回全零;如果是有意移除,在声明中如实填写)。建议:声明了 AD_ID——Play Console 的广告 ID 声明和 Data safety 中的"设备或其他 ID"必须一致。 | Google Play |
PRIV-04 | 不可重置的设备标识 | 高风险 | 高风险:AndroidX / Google 库以外的代码调用了 TelephonyManager 的 getDeviceId / getImei / getMeid / getSubscriberId / getSimSerialNumber 或 Build.getSerial。建议:这些调用只出现在 AndroidX / Google 库代码中(兼容性封装)、引用了 Build.SERIAL 字段、Settings.Secure.getString 加 "android_id" 字符串(SSAID),或只声明了 READ_PHONE_STATE / READ_PRIVILEGED_PHONE_STATE。DEX 扫描不完整时为未知。 | Google Play / Play 以外(线下分发) |
PRIV-05 | 采集已安装应用列表 | 高风险 | 高风险:DEX 代码调用 getInstalledPackages / getInstalledApplications,申请了 QUERY_ALL_PACKAGES,并且有广告或统计 SDK。建议:有调用,但另外两项只占其一。DEX 扫描不完整且没找到时为未知。 | Google Play / Play 以外(线下分发) |
PRIV-06 | 发现登录功能:账号删除要求 | 建议 | 仅 Play。识别到登录类 SDK(Firebase Authentication、Google 登录 / Credential Manager、Facebook Login、微信、QQ、微博、LINE 等)。只是提醒:工具无法判断应用是否允许注册账号。不扫描资源字符串(如"注册")。 | Google Play |
PROT-02 | 未开启 R8 优化 / 混淆 | 建议 | 找到 ~~R8 标记(R8 写入 DEX 字符串池)为通过。只有 ~~D8 标记(未压缩代码;会指出 "compilation-mode":"debug"),或没有任何标记且 50 个以上类中 1–2 个字母的 R8 式类名不到 5% 时为建议。列出仍带 DEBUG 字段的 BuildConfig 类。没有标记且启发式无法判断时为未知。 | Google Play / Play 以外(线下分发) |
PROT-03 | Play Integrity API | 建议 | Play 场景:没有找到 com.google.android.play.core.integrity 下的类时为建议。仅线下:找到时为建议(Play 以外安装的副本会被判为未授权)。仍调用 SafetyNet Attestation API(已于 2025 年 1 月关停)时一律为建议。 | Google Play / Play 以外(线下分发) |
PROT-04 | 运行时动态加载 dex 或 so | 拒审 | Play:运行时加载代码(DexClassLoader、InMemoryDexClassLoader、PathClassLoader、System.load / Runtime.load)且存在远程 .dex/.jar/.so 地址字符串为拒审;Google / AndroidX 库以外调用 DexClassLoader / InMemoryDexClassLoader 为高风险;其他情况(只有 PathClassLoader 或 System.load、只在已知库中、assets/ 中带代码文件)为建议。Play 以外同样的发现最高为高风险。 | Google Play / Play 以外(线下分发) |
体检前先选择分发场景:Google Play、Play 以外(线下分发或其他应用商店),或者两者都要。只在 Play 以外分发时,权限声明表这类 Play 专属规则会跳过;SIDE-01 这类安装层面的规则在任何场景下都适用。
你还可以选择“按哪一天生效的规则体检”,默认是今天。选一个以后的日期,就能提前看到哪些要求即将生效,例如 2027-02-01 之后的 16 KB 规则。
代码(DEX)、SDK 与签名
分析器也会读取编译后的代码。所有 classes*.dex 都会解析:APK 中在根目录,AAB 中在 base/dex/ 以及各功能模块的 dex/ 目录。支持 DEX 035 到 041(包括 041 容器格式),遇到其他版本会如实报告。每个文件都会校验头部的校验和(Adler-32)和 SHA-1 签名,不一致时给出警告。
工具不做任何反编译,只从代码中收集以下信息:
- 框架 API 的引用和调用位置:通过扫描
invoke-*指令找到,例如PackageInstaller、DexClassLoader、TelephonyManager.getImei、getInstalledPackages。结果会写明是哪个类调用的,而不只是“存在引用”。 - 少量字符串特征:
.apk下载地址,.dex/.jar/.so下载地址,安装 intent 的 MIME 类型,以及android_id。 - D8/R8 编译器标记:用来区分经过压缩的 release 构建和普通构建。
- 类名:用于 SDK 指纹(如
com.google.firebase.*、io.sentry.*、com.applovin.*);少数开源 SDK 会在类里写版本常量,工具也会读取。
这些信息用于以下代码级检查:
- 自更新(PLAY-12、SIDE-05)
- SDK 清单与 Data safety 草稿(PRIV-01、PRIV-02)
- 广告 ID(PRIV-03)
- 设备标识(PRIV-04)
- 已安装应用清单(PRIV-05)
- 登录与账号删除(PRIV-06)
- R8(PROT-02)
- Play Integrity(PROT-03)
- 运行时加载代码(PROT-04)
“代码(DEX)“下面的 类和方法 浏览器按包列出所有类,以及每个类的字段、方法和访问标志,可以按名称筛选。列表有显示上限,展开时才渲染,所以超大应用也不会卡。
签名现在会做完整校验,而不只是读取:
- v2、v3、v3.1:重新计算按 1 MiB 分块的内容摘要,并用浏览器的 WebCrypto 验证签名(SIGN-07)。
- v1 / JAR 签名(包括 AAB 的上传签名):把
MANIFEST.MF中的每个摘要与对应文件逐一比对(SIGN-06)。 - 与上一版本比较:点击 与上一版本比较签名 并加入上一版 APK 或 AAB,工具会检查两者是否使用同一密钥,或属于同一条 v3 轮换谱系(SIGN-09)。
Flutter 应用
如果包里有 Flutter(libflutter.so 或 flutter_assets/),报告会多出 Flutter 构建信息,全部从文件本身读取,读取量有上限:
- 每个 ABI:
lib/<abi>/libflutter.so(引擎)和libapp.so(AOT 编译后的 Dart 代码)在不在。AAB 里对应base/lib/<abi>/。 - Dart 快照哈希和 VM 特性:来自每个
libapp.so开头附近的快照头。哈希是 32 位字符,对应编译这个应用的 Dart SDK;工具只显示它,不换算成版本号。 - Dart 版本:编译在
libflutter.so里的版本字符串,例如3.11.1 (stable) (Tue Feb 24 00:03:07 2026 -0800) on "android_arm64"。 - 构建模式:
flutter_assets里有kernel_blob.bin、isolate_snapshot_data或vm_snapshot_data就是 debug(JIT)。release 还是 profile(AOT)看libapp.so里的第一个 VM 特性(product或release)。后一条规则在 Flutter 3.41.4 和 Flutter Gallery 2.9.2 的构建上核对过,其他版本请当作启发式判断。 flutter_assets在哪(assets/还是base/assets/)、有多少个文件,以及AssetManifest和FontManifest.json里的资源数、字体数。
每个 libapp.so 只读前 256 KiB,libflutter.so 只读一个、只读前 4 MiB。值不在这个范围里就不显示,不会去猜。想继续分析 Dart 代码,见下方相关教程里的 Flutter 那篇。
隐私是怎么保证的
文件不会被上传。这一点由页面的实现方式决定,而不只是一句承诺:
- 分析器运行在一个单独的框架(iframe)里,来源是
tools.rea-reverse.com,在你的设备上运行。 - 这个页面在正文下方展示一个广告。广告脚本运行在
rea-reverse.com上,而框架属于另一个源(origin),浏览器不允许广告脚本或本页的任何其他脚本读取你选择的文件或分析报告。 - 框架只把浏览器的
File句柄交给一个 Web Worker。Worker 按需切片读取文件,用浏览器内置的DecompressionStream解压条目,代码里完全没有网络请求。 - 只读取需要的部分:ZIP 中央目录、
AndroidManifest.xml、资源表(APK 的resources.arsc,AAB 的resources.pb)、启动图标、Manifest 指向的无障碍服务配置和网络安全配置 XML、每个.so和 React Native bundle 开头的一小段、很小的版本文件,以及签名数据。一个 200 MB 的 APK,实际只读几 MB。 - 报告显示之后,Worker 会按 4 MB 一段计算整个文件的 SHA-256,仍然在浏览器里完成。哈希显示在页面上,也会写进你自己导出的文件。旁边有一个在 VirusTotal 上搜索这个哈希的链接:它只是普通链接,你点击之前不会发出任何请求,点击后地址里也只有哈希。文件本身不会上传到任何地方。
- 框架的内容安全策略拦截它的脚本能发起的所有请求(
connect-src 'none',也不加载任何第三方资源)。框架不能自行打开窗口或让本页跳转;只有你点击报告里的链接时才会打开页面。它传给本页的只有自身高度和下面这些统计事件。 - 分析结束后发送的统计事件只包含粗粒度的固定取值:文件类型、大小区间、耗时区间、成功或错误码、未通过的检查数量。绝不包含文件名、包名、哈希或任何文件内容。框架把事件交给本页,由本页发送。完整清单见隐私政策。
- 不想要任何广告和统计?在单独页面打开分析器。
如何自己验证
你不必只听我们说。
- 打开浏览器开发者工具的 Network(网络) 面板,加载本页。发往
tools.rea-reverse.com的请求就是框架:它的 HTML、样式、字体和脚本,以及 Worker 脚本/_astro/apk-worker-*.js。发往 Google 域名的请求是本页的广告。 - 清空日志,然后选择文件并完成一次分析。在过滤框里输入
domain:tools.rea-reverse.com:新出现的请求只有来自tools.rea-reverse.com的 Worker 脚本和它加载的代码块:工具会为下一个文件预先启动一个新的 Worker。它们都不带你的文件。广告请求可能还会继续,但它们来自本页,不带框架里的任何内容。 - 点开来自
tools.rea-reverse.com的apk-analyzer/文档,查看 Headers:它的Content-Security-Policy里有connect-src 'none'和frame-ancestors https://rea-reverse.com。在 Chrome 里,Application(应用)> Frames 也能看到框架的源和策略。Worker 运行在default-src 'none'; script-src 'self'; connect-src 'none'这条策略下。 - 检查隔离:在 Console(控制台) 里,上下文选
top,运行document.getElementById('tool-frame').contentDocument,结果是null;frames[0].document会抛出SecurityError。广告脚本拿到的也是这个结果。 - 或者先打开页面再断网,然后做分析,照样能用。
结果怎么看
每条检查的结果是以下五种状态之一:
- 拒审:如果该规则适用于你选择的场景,Google Play 会阻止发布,或者 Android 会拒绝安装。
- 高风险:大概率触发权限声明审核、Play Protect 警告,或者是一项已公布生效日期、即将强制执行的要求。
- 建议:最佳实践或提前预警。
- 未知:工具无法判断。常见原因是某个值是工具暂时无法解析的资源引用,或者规则依赖的那部分文件解析失败。“未知”不等于通过,请手动核对。
- 通过:工具检查过,没有发现问题。
每条未通过的结果都会列出证据,例如 lib/arm64-v8a/libfoo.so: PT_LOAD p_align=0x1000,方便你在动手修改之前先用自己的工具复核。
报告全部通过也不等于审核通过。Google Play 还会审核权限声明、商店详情、Data safety 表单和运行时行为,这些都不是检查文件能看到的。本工具的结果不代表 Google 的审核结论。 如果想在 Android Studio 里检查源码层面的政策风险,Google 还提供了 Play Policy Insights。
常见问题怎么修
16 KB 页面大小(PLAY-02、PLAY-03)
以 Android 15 及以上为目标的应用,必须在 64 位设备上支持 16 KB 内存页。Google 开发者文档写明,自 2027-02-01 起,不合规的更新将无法发布。在那之前,工具把这两项标为“高风险”,而不是“拒审”。来源:Support 16 KB page sizes。
- ELF 段对齐(PLAY-02):用 NDK r28 或更高版本编译原生代码,默认就是 16 KB 对齐。NDK r27 及以下,添加链接参数
-Wl,-z,max-page-size=16384。第三方 SDK 提供的预编译.so,请向供应商索取支持 16 KB 的版本。 - ZIP 内对齐(PLAY-03):升级到 Android Gradle Plugin 8.5.1 或更高版本,它会正确对齐未压缩的库。暂时无法升级的,设置
useLegacyPackaging = true改为压缩存放(安装体积会变大)。修改后可以用zipalign -c -P 16 -v 4 app.apk复核。
targetSdk 36(PLAY-01)
自 2026-08-31 起,面向手机和平板的新应用和应用更新必须以 API 36(Android 16)为目标,可以在 Play Console 申请延期,最晚到 2026-11-01。Wear OS 和 Android Automotive OS 应用要求 35,TV 和 XR 应用要求 34;工具会读取 Manifest 中的 uses-feature 来判断设备形态并选用对应的门槛。来源:目标 API 级别要求。
提高 targetSdk 会改变应用的运行时行为,发布前请对照 Android 16 的行为变更逐项回归测试。反过来,SIDE-01 在 Play 以外同样重要:Android 14 拒绝安装 targetSdk 低于 23 的应用,Android 15 及以上拒绝安装低于 24 的应用(Android 15 行为变更)。
调试证书(SIGN-01)
主题为 CN=Android Debug,O=Android,C=US 的证书,是 Android Studio 为本地构建自动生成的调试密钥。Google Play 会拒绝它;而且它的私钥存在于每一台开发机上,任何人都能用它签出一个“更新”来替换你的应用。请生成专用的发布密钥(RSA 2048 位或以上),做好备份,用它签署发布版本。在 Play 上分发的,加入 Play 应用签名,由 Google 保管应用签名密钥,你自己保留上传密钥。来源:为应用签名、Play 应用签名。
只有 v1 签名(SIGN-03)
v1(JAR)签名只对 ZIP 中的各个条目签名,压缩包的其余部分不受保护。以 API 30 及以上为目标的应用必须使用 APK 签名方案 v2 或更高版本签名,否则无法安装。Android Gradle Plugin 默认启用 v2 和 v3,所以只有 v1 的 APK 通常来自自定义的签名步骤。请用 apksigner 签名并保持 v2/v3 开启;minSdk 达到 24 以后可以去掉 v1。注意顺序:先 zipalign,再 apksigner,不能反过来。来源:APK 签名、apksigner、zipalign。
权限类结果(PLAY-08、09、10、12、13、16)
这些结果并不意味着权限本身一定被拒,而是说明 Google Play 只允许这些权限用于特定的核心功能,并要求填写声明表。应用用不到的,就删除,包括由第三方库合并进 Manifest 的那些。确实需要的,优先换成范围更窄的替代方案:用 SMS Retriever API 代替短信权限,用 <queries> 代替 QUERY_ALL_PACKAGES,用存储访问框架(SAF)或 MediaStore 代替所有文件访问,用系统照片选择器代替 READ_MEDIA_IMAGES。另外,自 2027-01-27 起,“电话账户验证”不再是 READ_CALL_LOG 的允许用途(政策截止日期)。报告中的每条结果都会链接到对应的政策页面:短信和通话记录、软件包可见性、所有文件访问、REQUEST_INSTALL_PACKAGES、后台位置、照片和视频权限。
AAB 文件,以及如何用 bundletool 交叉核对
Android App Bundle 的 Manifest 和资源表用 protobuf 存储,而不是二进制 XML。工具会解码 base 模块的 manifest/AndroidManifest.xml、resources.pb 和 BundleConfig.pb,所以 AAB 和 APK 走同一套 Manifest 检查。@string/app_name、@mipmap/ic_launcher 这样的资源引用会解析成默认配置下的值,并与原始资源 ID 一起显示。报告还会列出 bundle 中的模块(base、功能模块和资源包,以及各自的分发方式),以及 BundleConfig.pb 中与 16 KB 适配相关的设置:原生库是否不压缩存放、按多大的页对齐。
启动图标是 PNG 或 WebP 位图时会显示出来,取密度最高的一张。自适应图标是矢量 XML,工具不做渲染,会明确标出;如果它的前景层是位图,就显示前景层。
想用 Google 自己的工具核对结果,可以在本地运行 bundletool:
java -jar bundletool.jar dump manifest --bundle=app.aab
java -jar bundletool.jar dump resources --bundle=app.aab --values
java -jar bundletool.jar dump config --bundle=app.aab
包名、versionCode、versionName、min/target SDK、权限、debuggable、应用名称和图标都应与报告一致。bundletool 按名称打印引用(如 @string/app_name),资源转储会给出这个名称对应的资源 ID(即报告里显示的 ID)和它的取值。
做不到的事
- 不做病毒判定:不扫描病毒,也不判断应用是否安全可装。
- 不反编译 DEX:不会把
classes.dex还原成 Java 或 Kotlin 代码,除invoke-*指令的目标外不解读字节码。需要的话,请在本地使用 JADX 等反编译工具(见下方相关教程)。 - 不转换 AAB:无法在浏览器里把 AAB 转成可安装的 APK,请使用
bundletool。工具按 bundle 原样读取,不会生成拆分 APK,所以 ZIP 内对齐(PLAY-03)和“只有 v1 签名”(SIGN-03)两项只对 APK 检查。 - 只用静态证据:
- SDK 根据文件名、Manifest、版本文件和 DEX 类名前缀识别。R8 可能会重命名或重新打包 SDK 的类,所以没列出的 SDK 也可能存在。只有包里写明了版本才显示版本号。
- 代码检查只看引用和调用了哪些 API、存在哪些字符串,不跟踪数据流,也看不到运行时拼接或下载的代码。
- DEX 扫描不完整时,依赖“不存在”做判断的规则会显示为未知。
- Data safety 清单只是用来对照的草稿。
- SDK Index 没有公开 API,所以不检查 SDK 的政策标记。
- 加壳只识别,不处理:加壳和加固是正当的防护手段。PROT-01 只说明识别到了什么,并解释误报和 Play App Signing 方面的风险。工具不会脱壳、修改、重签名或重新打包任何文件,也不提供任何向审核或安全扫描隐藏行为的方法。
- 签名校验的范围:
- DSA 签名 WebCrypto 无法验证,显示为未知。
- v3.2(Android 17 的 ML-DSA 混合签名)不做校验。
- v1 只核对摘要,不验证
.SF上的 PKCS#7 签名。 - v3 轮换谱系会读取,但不验证谱系本身的签名。
- 最终以
apksigner verify -v为准。
- 不代表审核结论:结果不代表 Google 的审核结论,报告全部通过也不保证能上架。
对应的命令行
用 Android SDK build-tools 36.1.0 里的 aapt2 读 Manifest(有删节):
$ aapt2 dump badging com.termux.window_1001.apk
package: name='com.termux.window' versionCode='1001' versionName='0.17.0' platformBuildVersionName='15' platformBuildVersionCode='35' compileSdkVersion='35' compileSdkVersionCodename='15'
minSdkVersion:'24'
targetSdkVersion:'28'
uses-permission: name='android.permission.INTERNET'
uses-permission: name='android.permission.SYSTEM_ALERT_WINDOW'
uses-permission: name='android.permission.VIBRATE'
…
native-code: 'arm64-v8a' 'armeabi-v7a' 'x86' 'x86_64'手动核对 PLAY-02:解压出库文件,用 NDK r29 的 llvm-readelf 看程序头。两个 LOAD 段都按 0x1000(4 KB)对齐,而不是 0x4000:
$ llvm-readelf -lW lib/arm64-v8a/libtermux.so
…
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
…
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x001bc8 0x001bc8 R E 0x1000
LOAD 0x001cc8 0x0000000000002cc8 0x0000000000002cc8 0x000338 0x000338 RW 0x1000
…16 KB 检查的 ZIP 部分(PLAY-03)。这些库是压缩存储的,不存在需要对齐的偏移,zipalign 通过:
$ zipalign -c -P 16 -v 4 com.termux.window_1001.apk
Verifying alignment of com.termux.window_1001.apk (4)...
…
819914 lib/arm64-v8a/libtermux.so (OK - compressed)
823566 lib/armeabi-v7a/libtermux.so (OK - compressed)
832435 lib/x86/libtermux.so (OK - compressed)
836192 lib/x86_64/libtermux.so (OK - compressed)
Verification successful签名和签名证书。minSdk 为 24 及以上时 apksigner 不校验 v1;报告列出 v1 签名者,是因为 APK 里有 META-INF/84D3000E.RSA:
$ apksigner verify --print-certs -v com.termux.window_1001.apk
Verifies
Verified using v1 scheme (JAR signing): false
Verified using v2 scheme (APK Signature Scheme v2): true
Verified using v3 scheme (APK Signature Scheme v3): true
…
Number of signers: 1
Signer #1 certificate DN: CN=FDroid, OU=FDroid, O=fdroid.org, L=ORG, ST=ORG, C=UK
Signer #1 certificate SHA-256 digest: 228fb2cfe90831c1499ec3ccaf61e96e8e1ce70766b9474672ce427334d41c42
Signer #1 certificate SHA-1 digest: e8754a2c614392071d27021bbcdfb8dda7e0711c
…$ apksigner verify --min-sdk-version 23 -v com.termux.window_1001.apk
Verifies
Verified using v1 scheme (JAR signing): true
Verified using v2 scheme (APK Signature Scheme v2): true
Verified using v3 scheme (APK Signature Scheme v3): true
…常见问题
用这个在线 APK 分析工具,文件会上传吗?
不会。文件由当前标签页里的 Web Worker 读取,不会发往任何服务器。可以在开发者工具的 Network 面板里确认,也可以先打开页面、再断网分析一个文件。
APK 检测支持哪些文件?
最大 1 GiB 的 .apk 和 .aab。暂不支持拆分 APK 集合(.apks、.xapk)。
怎样检查 APK 是否满足 16 KB 页面大小要求?
把文件拖进来,看 PLAY-02、PLAY-03 和原生库表:每个 64 位 .so 的 PT_LOAD 段对齐值至少要 0x4000,未压缩存储的库在 ZIP 里的偏移还要按 16 KB 对齐。上面 Termux:Float 的例子里,四个库都只按 0x1000 对齐。
能在线查看 APK 签名吗?
能。签名一节列出找到的签名方案(v1、v2、v3、v3.1),以及每个签名者的主题、有效期、密钥和证书的 SHA-256、SHA-1,也就是 apksigner verify --print-certs 打印的那些值。SIGN-01 到 SIGN-09 会标出调试证书、公开测试密钥、弱密钥和被篡改的签名。
为什么会出现“未知”?
检查所依赖的某个值读不出来,例如 targetSdkVersion 写成了资源引用,或者签名块已损坏。请把“未知”理解为需要你自己核对,而不是通过。
报告全部通过,是不是就一定能上架 Google Play?
不是。Play 还会审核权限声明、商店详情、Data safety 和运行时行为,工具只覆盖能从文件里读出来的部分。每条规则都记录了最近一次对照官方来源核实的日期,超过 90 天未复核的会标注“待复核”。
更新于
相关教程
- Android 16 KB 页面大小适配:在线检查 APK 的 .so 对齐:用示例 APK 逐条讲解 PLAY-02 和 PLAY-03,并用
zipalign、llvm-readelf复核。 - 用 REA 分析 Android APK:JADX、Apktool、ADB 与 Flutter 快照:从体检报告出发,找到导致问题的那段代码。
- Android CTF 入门:逆向一道练习 APK 题,找出 flag:用 DEX 类浏览器定位练习题的校验方法,再到 JADX 里读懂并解出 flag。
- Flutter APK 逆向分析:用 REA 读 libapp.so 的 Dart 快照哈希与依赖:报告识别出 Flutter 之后,从
libapp.so读出 Dart 快照哈希、引擎 Build ID 和依赖包 URI。 - AAB 转 APK 与 APK 解包:用 REA 盘点并提取资源和脚本:用
bundletool转换 AAB,并盘点包内的每一个条目。 - 安装包与现有包冲突怎么解决:排查自己 App 的签名不一致:对比两个构建的签名证书 SHA-256,修好签名不一致。
- 安装包似乎无效怎么解决:查出自己的 APK 到底哪里坏了:把笼统的安装提示还原成真实的 INSTALL_PARSE_FAILED 代码,再按签名(SIGN-03)、resources.arsc(SIGN-08)或拆分包(SIDE-08)逐项修好。
- 应用与你的手机不兼容:检查 targetSdk、ABI 和 minSdk:结合真实的 INSTALL_FAILED 代码,读懂 SIDE-01、PLAY-04 和原生库表格。
- Android 开发者验证:找出包名和签名证书 SHA-256,提前完成登记:从自己的 APK 或 AAB 里拿到登记需要的信息。
- 自己的 App 被 Play Protect 拦截?开发者视角的原因排查与正当处理:读懂 SIDE-02、SIDE-03、SIDE-06 和 PROT-01,修好构建,再走官方申诉。
- 用 REA 读 logcat 和 bugreport,排查自己 App 的崩溃:安装包能装上却会崩溃时,读崩溃日志和原生 tombstone。
- REA MCP 安装指南:接入 Claude Code、Cursor 与 Codex:在本地装好 REA,做更深入的分析。
- REA 安全吗?如何确认装的是真正的 Reverse Engineer Anything:确认你安装的是正版的 REA 软件包。
- JADX、JEB、Apktool 怎么选:三款 Android 反编译工具对比:需要看某条发现背后的代码时,下一步该打开哪款反编译器。
- 不认识的文件:用魔数识别文件类型:先分清 APK 和 NuGet 包、JAR、Office 文档等其他 ZIP 格式,再放到这里分析。