读懂 Apktool ApkInfo:APK 元数据的存储、加载与回写
读懂 Apktool ApkInfo:APK 元数据的存储、加载与回写
【免费下载链接】ApktoolA tool for reverse engineering Android apk files项目地址: https://gitcode.com/GitHub_Trending/ap/Apktool
反编译一个 APK 后,你随手删掉了输出目录里的apktool.yml,再跑apktool b,直接报错。你删掉的不是一份配置文件,而是 Apktool 重打包所依赖的 APK 元数据,它的存取逻辑都集中在 ApkInfo.java 这一个类里。
apktool.yml里到底存了什么
反编译时,Apktool 会在输出目录写出一份apktool.yml,记录 APK 反编译过程中提取到的关键信息,这就是 APK 反编译元数据的落盘形式。ApkDecoder 解析 AndroidManifest.xml 和 resources.arsc 时,会把 ApkInfo 对象逐字段填好,最后 save 到磁盘;apktool b重打包时,第一步就是读回这个文件。所以元数据的流向是:原始 APK → 内存对象 → yml → 内存对象 → 新 APK。丢了这份文件,重建就失去了对原始包的描述。
类的顶层字段和文件里的键一一对应,下表是各字段的说明:
| 字段 | 含义 | 典型值 |
|---|---|---|
version | 反编译所用 Apktool 版本 | 2.9.0 |
apkFileName | 原始 APK 文件名 | demo.apk |
usesFramework | 应用依赖的框架(ids 与 tag) | ids: [1] |
usesLibrary | 应用声明的共享库 | org.apache.http.legacy |
sdkInfo | 最低 / 目标 / 最高 SDK 版本 | minSdkVersion: 25 |
versionInfo | 应用版本号与版本名 | versionCode: 10 |
resourcesInfo | resources.arsc 的包 ID、包名与重建标志 | packageId: 127 |
featureFlags | 功能开关 | 通常为空 |
doNotCompress | 不参与压缩的文件类型列表 | arsc |
下面是一份典型的apktool.yml,关键字段加了注释:
# apktool.yml:Apktool 反编译时写出的 APK 元数据快照 version: 2.9.0 # Apktool 版本 apkFileName: demo.apk # 原始 APK 文件名 usesFramework: # 框架依赖 ids: [1] # 1 表示默认 Android 框架 sdkInfo: # SDK 版本范围 minSdkVersion: 25 targetSdkVersion: 33 versionInfo: # 来自 AndroidManifest.xml 的版本信息 versionCode: 10 versionName: 1.2.0 resourcesInfo: # 重建 resources.arsc 的参数 packageId: 127 # 资源包 ID,127 即 0x7f sparseEntries: false doNotCompress: # 不压缩的文件类型 - arsc注意两点:version字段记录写出这份文件的 Apktool 版本,yml 的约定在不同大版本间有过变化,靠它判断文件来源;另外回写时 ApkInfo 只写非空字段,某个子对象没值,对应的键在文件里就干脆不出现。
从磁盘到内存,再到磁盘:load 和 save 各做了什么
ApkInfo 的 load 和 save 是 APK 元数据与磁盘之间往返的两个入口,下面的代码是最常见的用法:读进来、改一个字段、再写回去。
ApkInfo apkInfo = ApkInfo.load(new File("demo")); // 读 apktool.yml apkInfo.setApkFileName("demo-modified.apk"); // 修改字段 apkInfo.save(new File("demo")); // 写回load(File)会到指定目录下找apktool.yml,用 YamlReader 逐行解析;load(InputStream)用于从流里读取,一般只在测试中出现。save(File)与 load 对称,把各字段经 YamlWriter 序列化回同名文件。文件缺失时 load 会直接抛 AndrolibException,开头那个报错就是这么来的。
还有一个细节:解析apkFileName时会检查值里是否包含/或\这类路径分隔符,命中就抛 SecurityException,防止恶意构造的 yml 把路径指到目录之外。
四个子对象各管什么
UsesFramework 记录应用依赖的框架,包括 ids 列表和可选的 tag。普通应用一般是 ids 为 [1],1 表示系统框架;框架应用则有自定义 tag,重打包时要靠它定位对应的 framework-res.apk。
SdkInfo 保存清单里的三个 SDK 版本字段:minSdkVersion、targetSdkVersion、maxSdkVersion。除了纯数字,它也能把 "N"、"T" 这类字母代号解析成对应数字。
VersionInfo 是从 AndroidManifest.xml 读出的 versionCode 与 versionName,重打包时 Apktool 允许在构建参数中覆盖这两个值。
ResourcesInfo 记录重建 resources.arsc 的关键参数:packageId(通常是 127,即 0x7f)、packageName,以及 sparseEntries、compactEntries、keepRawValues 三个标志。这些标志决定重建时是否采用稀疏或紧凑结构,需要与原 APK 保持一致。
重打包时它替你守住了什么 🛡️
负责重打包的 ApkBuilder 会从这些字段取走大部分构建参数。随便改一个字段,问题通常会在构建时或运行时暴露:
把arsc从 doNotCompress 里删掉,构建时 resources.arsc 会被压缩着写进 zip,老版本 Android 无法对压缩后的资源文件做内存映射,资源加载可能直接失败。
把 resourcesInfo 里的 packageId 从 127 改成别的值,smali 和清单里 0x7f0xxxxx 形式的资源引用会全部对不上,应用一般启动即崩。
框架应用改动了 usesFramework 的 ids 或删掉 tag,重打包时找不到正确的 framework-res.apk,要么构建报错,要么产出缺资源的包。
sdkInfo 同理:minSdkVersion 设得比目标设备 API 高,应用直接装不上。稳妥的做法是——只改资源或代码时,这份文件就别动,或只动你确知用途的字段。
调试时怎么确认元数据没丢
怀疑某个字段在 load/save 中丢失时,可以翻 brut.apktool/apktool-lib/src/test/java/brut/androlib/meta/ 目录下的测试:ApkInfoReaderTest 验证标准 YAML 的解析,ApkInfoSerializationTest 验证 load 与 save 的往返一致性。
在「反编译 → 修改 → 重打包」管线里,ApkInfo 就是逐级传递的元数据快照;apktool.yml还在,包就能重建。
【免费下载链接】ApktoolA tool for reverse engineering Android apk files项目地址: https://gitcode.com/GitHub_Trending/ap/Apktool
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
