解压 APK 后打开 AndroidManifest.xml,看到的是二进制:Android 会把每个 XML 资源编译成自己的二进制 XML 格式(AXML),带字符串池和属性资源 ID。aapt2 dump xmltree 和 apktool d 可以把它还原成文本,但前者要装 Android SDK,后者要 Java,而且 apktool 会解码整个应用,哪怕你只想看 Manifest。
这个解码器在浏览器里运行。拖入你解压出的 Manifest、任意其他编译过的 XML、resources.arsc 资源表,或者整个 APK / AAB 都可以。
使用步骤
- 直接用 APK 或 AAB,或者先解压(
unzip app.apk AndroidManifest.xml resources.arsc)取出你要的二进制文件。布局等编译过的 XML 在res/下。 - 把它拖到上方的拖放区,或点“选择文件”。
- 先看“概要”(包名、版本、SDK 级别、应用名称),再看解码后的 XML。如果是 APK,可以在“其他 XML 文件”里选任意文件点“解码”,或展开 resources.arsc 的完整输出。
真实输出示例
我们把 F-Droid 上 Ves Android Image Compare 的构建(版本 51,SHA-256 ba280b33…6ef6)拖进本页,复制了页面显示的内容。版式有压缩(标签和值放在一行,省略按钮,Manifest 和展开后的 resources.arsc 输出在标记处有删节),所有值原样保留。“解码耗时”每次运行都不同。
样本: com.vincentengelsoftware.vesandroidimagecompare_51.apk,F-Droid 上的开源应用(2026-10-13 下载) (f-droid.org/packages/com.vincentengelsoftware.vesandroidimagecompare/)
概要
输入 APK
大小 1.8 MB
根元素 <manifest>
元素数 57
包名 com.vincentengelsoftware.vesandroidimagecompare
版本名(版本号) 2.3.6 (51)
SDK 最低 28 · 目标 36
应用名称 vesIC
申请的权限 3
组件 activity × 15 · provider × 2 · receiver × 1
资源表 15 种类型共 2840 个资源
资源 ID 命名 是:@0x7f… 引用写成 @type/name
解码耗时 22 ms
解码后的 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest android:versionCode="51" android:versionName="2.3.6" android:compileSdkVersion="36" android:compileSdkVersionCodename="16" package="com.vincentengelsoftware.vesandroidimagecompare" platformBuildVersionCode="36" platformBuildVersionName="16" xmlns:android="http://schemas.android.com/apk/res/android">
<uses-sdk android:minSdkVersion="28" android:targetSdkVersion="36" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<permission android:name="com.vincentengelsoftware.vesandroidimagecompare.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" android:protectionLevel="0x00000002" />
…
<application android:theme="@style/Theme.VesAndroidImageCompare" android:label="@string/app_name_short" android:icon="@mipmap/ic_launcher" android:fitsSystemWindows="false" android:allowBackup="true" android:largeHeap="true" android:supportsRtl="true" android:extractNativeLibs="false" android:roundIcon="@mipmap/ic_launcher" android:appComponentFactory="androidx.core.app.CoreComponentFactory" android:localeConfig="@xml/locales_config">
…
<activity android:name="com.vincentengelsoftware.androidimagecompare.ui.main.MainActivity" android:exported="true" android:launchMode="2">
<intent-filter android:label="@string/app_name_short">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
…
resources.arsc(2840 个资源)
…
<mipmap name="ic_launcher" id="0x7f0d0000" config="mdpi">res/d2.webp</mipmap>
…
<mipmap name="ic_launcher" id="0x7f0d0000" config="anydpi">res/BW.xml</mipmap>
…
<string name="app_name_short" id="0x7f0f001e">vesIC</string>
<string name="ask_for_review_negative" id="0x7f0f0020">Don't show up again</string>
<string name="ask_for_review_negative" id="0x7f0f0020" config="de">Nicht nochmal anzeigen</string>
…能读什么
- 二进制 XML(以
03 00 08 00开头):AndroidManifest.xml、res/layout/*.xml、drawable、res/xml/下的配置(例如网络安全配置)。文本 XML 会被识别并拒绝,这样你就知道这个文件根本没被编译过。 - resources.arsc:APK 的资源表。
- APK:从归档中取出 Manifest,并用 APK 自己的
resources.arsc给@0x7f…引用命名(@string/app_name、@style/AppTheme)。APK 里其他编译过的 XML 文件会列出来,选一个点“解码”即可。 - AAB:解码
base/manifest/AndroidManifest.xml中的 protobuf Manifest,并用base/resources.pb命名引用。 - 归档通过 ZIP 目录读取(支持 ZIP64):只解压用到的条目,所以 500 MB 的 APK 也只比它的 Manifest 多花一点时间。
能看到什么
- 概要:输入类型、根元素和元素数;如果是 Manifest,还有包名、版本名和版本号、min / target SDK、解析成默认字符串的应用名称、权限数量和按类型统计的组件。
- 解码后的 XML:命名空间声明在根元素上;属性名取自
android.R.attr资源 ID(与aapt2 dump xmltree打印的名称一致);整数为十进制、标志位为十六进制、布尔值为true/false;资源表里有的引用写成@type/name,没有的(框架资源)保持@0x…十六进制。 - resources.arsc:每种类型的资源数,以及整张表的 XML,每个资源的每个配置各占一行:
<string name="app_name" id="0x7f030000" config="fr">…</string>。文件资源显示它在 APK 中的路径,引用显示@type/name。
“下载”和“复制”可以保存 XML。输出很大时,页面上只显示前 1 MiB。
我们怎么验证的
解析器就是 APK 分析器用的那一套,已在自建样本(test/apk/fixture.apk、fixture.aab)和 F-Droid 上的开源 APK 上与 aapt2 dump xmltree、aapt2 dump resources、aapt2 dump badging 对照测试。为这个页面,我们解码了 F-Droid 上 Ves Android Image Compare(版本 51)的构建,把 Manifest 的每个属性与 aapt2 dump xmltree 比对,并把每个命名后的引用与 aapt2 dump resources 给出的资源 ID 比对。
你的数据如何保持私密
- 解码器运行在一个单独的框架(iframe)里,来源是
tools.rea-reverse.com;解码在框架里的 Web Worker 中、在你的设备上完成。拖入的文件不会上传。 - 这个页面在正文下方展示一个广告。广告脚本运行在
rea-reverse.com上,而框架属于另一个源(origin),浏览器不允许广告脚本或本页的任何其他脚本读取你拖入的文件或解码结果。 - 框架的内容安全策略拦截它的脚本能发起的所有请求(
connect-src 'none',也不加载任何第三方资源)。框架也不能自行打开窗口或让本页跳转。它传给本页的只有自身高度和下面这些统计事件。 - 文件中的标签、属性和值都以纯文本显示,其中的 URL 不会被请求。
- 统计事件只包含粗粒度的值:成功或错误码、输入类型、资源数区间、大小区间和耗时区间,不含包名、值或资源名。框架把事件交给本页,由本页发送。完整列表见隐私政策。
- 不想要任何广告和统计?在单独页面打开解码器。
如何自行验证
- 打开浏览器开发者工具的 Network(网络) 面板,加载本页。发往
tools.rea-reverse.com的请求就是框架:它的 HTML、样式、字体和脚本,以及 Worker 脚本/_astro/axml-worker-*.js。发往 Google 域名的请求是本页的广告。 - 清空日志,然后拖入一个 APK。在过滤框里输入
domain:tools.rea-reverse.com:最多只会再出现一次 Worker 脚本/_astro/axml-worker-*.js(为下一次运行准备的新 Worker),没有任何请求带着你的数据。广告请求可能还会继续,但它们来自本页,不带框架里的任何内容。 - 点开来自
tools.rea-reverse.com的android-manifest-decoder/文档,查看 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。广告脚本拿到的也是这个结果。 - 或者先打开页面再断网,然后拖入文件,照样能用。
- 用
aapt2 dump xmltree --file AndroidManifest.xml app.apk对照。
局限
- 只解码,不重新编码、编辑或重新打包 APK。
resources.arsc中的 style、array 和 plurals 只列出名称,值显示为(complex),不展开其中的条目。- 配置只按语言、屏幕密度和 SDK 级别命名(
fr、xxhdpi、anydpi-v26)。其他限定符(地区、夜间模式、屏幕尺寸)显示为+…。 - 框架资源引用(
@0x0103…)保持十六进制,与 aapt2 的输出一致。AAB 的resources.pb只用于命名引用,不会整表输出。
对应的命令行
Android SDK build-tools 里的 aapt2(2.20-13193326)把同一份 Manifest 打印成树,引用保持十六进制 ID(有删节):
$ aapt2 dump xmltree --file AndroidManifest.xml com.vincentengelsoftware.vesandroidimagecompare_51.apk
N: android=http://schemas.android.com/apk/res/android (line=2)
E: manifest (line=2)
A: http://schemas.android.com/apk/res/android:versionCode(0x0101021b)=51
A: http://schemas.android.com/apk/res/android:versionName(0x0101021c)="2.3.6" (Raw: "2.3.6")
…
E: application (line=25)
A: http://schemas.android.com/apk/res/android:theme(0x01010000)=@0x7f1002b9
A: http://schemas.android.com/apk/res/android:label(0x01010001)=@0x7f0f001e
A: http://schemas.android.com/apk/res/android:icon(0x01010002)=@0x7f0d0000
…
A: http://schemas.android.com/apk/res/android:localeConfig(0x0101065b)=@0x7f120001
…这些十六进制 ID 要通过资源表才能对上名称(有删节):
$ aapt2 dump resources com.vincentengelsoftware.vesandroidimagecompare_51.apk
…
resource 0x7f0d0000 mipmap/ic_launcher
(mdpi) (file) res/d2.webp
…
resource 0x7f0f001e string/app_name_short
() "vesIC"
…
resource 0x7f1002b9 style/Theme.VesAndroidImageCompare
…
resource 0x7f120001 xml/locales_config
() (file) res/Br.xml type=XML
…常见问题
怎样在线解码 AndroidManifest.xml 又不上传 APK?
把 APK,或者从中解压出的 AndroidManifest.xml 拖到本页即可。解码在浏览器中一个没有网络权限的 Web Worker 里进行,文件不会发送到任何地方。
为什么解压 APK 后 AndroidManifest.xml 打不开?
因为 aapt2 把它编译成了 Android 二进制 XML(AXML):标签和属性名放在字符串池里,属性用资源 ID 标识,值带类型。APK 里 res/ 下的每个 XML 文件都是这样编译的,本页都能解码。
AXML 解析器是做什么的?
把 Android 二进制 XML 还原成文本:读取字符串池,把 0x01010003 这样的属性资源 ID 映射成 android:name 这样的名称,再把每个带类型的值写成文本。命令行里 aapt2 dump xmltree 和 apktool d 做的是同一件事。
能把 resources.arsc 转成 XML 吗?
能。拖入 resources.arsc 或包含它的 APK,整张表会写成 XML,每个资源的每个配置一行,带资源 ID。style、array 和 plurals 只列出,不展开。
为什么有些引用还是 @0x0103… 这样的十六进制?
那些是 Android 框架资源(包 ID 0x01)。它们的名称在系统的 android.jar 里,不在 APK 中,aapt2 也同样打印成十六进制。应用自己的资源(包 ID 0x7f)会用 APK 自带的资源表命名。
支持 AAB 文件吗?
支持。AAB 的 Manifest 以 protobuf 存放在 base/manifest/AndroidManifest.xml,会被解码,并用 base/resources.pb 给引用命名。资源表本身只对 APK 整表输出。
更新于
相关工具与指南
- AAB 转 APK 与 APK 解包:提取资源和脚本:需要从 APK 或 AAB 里拿出的不止 Manifest 时。
- APK/AAB 分析与上架体检:同一份 Manifest,按 Google Play 规则检查,还有原生库和签名证书。
- 用 REA、JADX 和 Apktool 分析 Android APK