6 步读懂 Apktool 的 ApkInfo 类:APK 元数据存储与加载完整指南
6 步读懂 Apktool 的 ApkInfo 类APK 元数据存储与加载完整指南【免费下载链接】ApktoolA tool for reverse engineering Android apk files项目地址: https://gitcode.com/GitHub_Trending/ap/Apktool当您用 Apktool 反编译一个 APK 后输出目录里总会静静躺着一个apktool.yml——它就是 ApkInfo 类的序列化产物。读懂这套 APK 元数据的存储与加载机制您就能解释重新打包为什么能成功还能安全地手工修改 APK 的关键属性。本文带您在 6 步内走完概念、原理、实战与避坑全流程。一张 apktool.yml 管住了 APK 的什么ApkInfo 是 Apktool 中负责承载 APK 应用元数据Metadata即描述应用本身的信息如版本、SDK 要求、框架依赖等的核心类源码位于 ApkInfo.java。它实现了项目自研的YamlSerializable接口定义在 brut.j.yaml 模块因此可以自由地对象 ↔ YAML 文本双向转换。打个比方ApkInfo 就像一栋楼竣工时的档案袋记录了楼高几层、电梯几部、承重多少。将来要按图纸翻修重新打包时施工队靠的就是这个档案袋而不是凭记忆。回到技术本身解码时 Apktool 把 APK 的各项关键信息写入apktool.yml编码时再读回来指导构建。它具体管理这几类信息身份信息生成该档案的 Apktool 版本号、原始 APK 文件名框架与库依赖APK 依赖的系统框架uses-framework和第三方库SDK 范围minSdk / targetSdk / maxSdk 三个版本号应用版本versionCode 与 versionName资源包信息packageId、packageName、稀疏/紧凑条目标志功能开关featureFlags 键值对压缩策略doNotCompress 不压缩文件清单数据从哪来又流向哪里理解 ApkInfo 的运行机制关键是看写入和读取两个方向的调用链。写入方向解码阶段ApkDecoder 在decode()过程中创建ApkInfo实例ResDecoder解析resources.arsc时填充资源包信息ManifestPullEventHandler解析清单时填充 SDK 与版本信息。最后writeApkInfo()会额外做两件事若未解析清单如--no-res模式用 smali 反编译推断出的 dex 指令集 API 级别兜底填入minSdkVersion扫描原 APK 中压缩级别为 0 的文件按扩展名优先、文件名兜底的原则汇总进doNotCompress列表然后调用mApkInfo.save(outDir)落盘为apktool.yml。读取方向编码阶段ApkBuilder 的build()一上来就执行ApkInfo.load(mApkDir)随后把minSdkVersion传给 smali 汇编保证 dex 字节码指令集与 SDK 匹配把AaptInvoker挂上 ApkInfo构建资源时读取 packageId、versionName、featureFlags、框架依赖等并在最终打 zip 包时应用doNotCompress清单。一个真实的apktool.yml长这样按write()方法的实际输出顺序排列version: 2.9.3 apkFileName: sample.apk usesFramework: ids: - 1 sdkInfo: minSdkVersion: 25 targetSdkVersion: 30 versionInfo: versionCode: 71 versionName: 1.0.70 resourcesInfo: packageId: 127 packageName: com.example.sample sparseEntries: true doNotCompress: - arsc - png注意各段是有内容才写出的UsesFramework、SdkInfo等对象内部都实现了isEmpty()检查空对象不会出现在 YAML 里所以您看到的文件总是精简的。实战加载、修改、保存的完整调用流程下面是一段可直接运行的 Java 调用示例演示如何以编程方式操作 ApkInfo依赖 apktool-lib 模块import brut.androlib.meta.ApkInfo; import brut.androlib.meta.SdkInfo; import java.io.File; // 从反编译目录加载 apktool.yml ApkInfo apkInfo ApkInfo.load(new File(sample-decoded)); // 查看并修改 SDK 目标版本 SdkInfo sdk apkInfo.getSdkInfo(); System.out.println(原 targetSdk: sdk.getTargetSdkVersion()); sdk.setTargetSdkVersion(33); // 修改后保存回 apktool.yml apkInfo.save(new File(sample-decoded));更常见的其实是命令行操作。修改apktool.yml后重新打包只需两步# 在反编译目录中手工编辑 apktool.yml 后执行 apktool b sample-decoded -o sample-modified.apkapktool b内部走的就是上面ApkBuilder.build()的加载链路您改的每一个字段都会真实影响构建结果。逐个组件看7 个字段各自管什么以下按apktool.yml中的出现顺序逐条讲透每个组成部分。1. version 与 apkFileNameversion记录生成档案的 Apktool 版本apkFileName是原始 APK 文件名编码时若未指定输出名它会作为dist/下产物文件名。源码中有一处值得注意的安全校验apkFileName的值若为.、..或包含路径分隔符直接抛出SecurityException防止恶意 YAML 诱导文件写出目录相关防御测试见 MaliciousYamlTest.java。2. usesFrameworkUsesFramework 记录 APK 依附的 Android 框架字段含义ids框架 ID 列表普通应用依赖 Android 本身时即[1]定制 ROM 应用可能依赖其他 IDtag框架版本标签用于匹配apktool if安装的多个同 ID 框架构建时AaptInvoker会据此拼出aapt2 --framework参数保证编译资源时引用的系统资源正确。3. usesLibrary字符串列表对应清单里uses-library声明的共享库如org.apache.http.legacy。源码中该列表是ListStringYAML 里就是普通的字符串数组。4. sdkInfoSdkInfo 持有minSdkVersion、targetSdkVersion、maxSdkVersion三个字符串。亮点在于parseSdkInt()既接受数字30也接受字母代号M、O、Q……直到最新的Baklava、Cinnamon Bun并映射为 ResConfig 中的常量。另有getTargetSdkVersionBounded()会把 target 收敛到[min, max]区间内供资源编译的 API 级别判定使用。5. versionInfoVersionInfo 保存versionCode整数与versionName字符串来自AndroidManifest.xml的application标签。注意getVersionCode()在未设置时返回-1而非 0这是未知的哨兵值。6. resourcesInfoResourcesInfo 包含五个字段packageId资源包 ID绝大多数应用为127十六进制0x7f若被其他框架合并过可能不是 127packageName资源包名通常与应用包名一致sparseEntries是否使用稀疏资源条目Android O 引入的资源压缩机制compactEntriesAndroid 9 起的紧凑资源条目keepRawValues是否保留原始资源值这三个布尔开关决定了重建resources.arsc时的二进制结构直接影响新系统上资源能否被正确读取。7. featureFlags 与 doNotCompressfeatureFlags是MapString, Boolean对应 aapt2 的--feature-flag编译选项doNotCompress是字符串列表条目可以是扩展名png、arsc也可以是具体文件路径编码时这些文件将以存储不压缩模式写入 APK保证系统能按偏移量直接读取resources.arsc。三个真实场景它替您解决了什么场景一只改文案、不动代码。您把 APK 反编译后只修改了res/values/strings.xml和一个布局apktool b能一次成功靠的就是apktool.yml里保存的 packageId、minSdk 和 doNotCompress 清单——框架选择、字节码指令集、压缩方式全部原样还原。场景二给应用抬高 targetSdk。商店要求更高 API 时您无需触碰任何 Java 代码解码、把sdkInfo.targetSdkVersion改为目标值、重新打包即可。smali 汇编级别和 aapt2 编译选项会自动跟着这份元数据走。场景三批量分析脚本。在 CI 中写一段 Java 程序对一批 APK 解码后只读ApkInfo不关心资源与 smali用getVersionInfo()、getSdkInfo()、getUsesLibrary()汇总成一份版本 / SDK / 依赖库报表。避坑指南4 个容易踩的雷别丢或手撕apktool.yml。它是ApkBuilder的强制输入文件缺失会直接报InFileNotFoundException手工编辑时保持 YAML 缩进正确键与列表项的缩进层级解析器是基于 YamlLine 的逐行结构解析缩进错乱会导致字段丢失或解析失败健壮性验证可参考 meta 测试目录。改 packageId 是高危操作。资源引用0x7f08xxxx形式的数值 ID与 packageId 绑定随意改成非 127 的值会导致大量资源引用失效除非您明确在做什么否则保持原值。doNotCompress 不是可有可无的装饰。移除arsc条目会让resources.arsc被压缩部分系统版本上应用会因无法按偏移读取资源而启动即崩溃新增大文件不压缩又会显著增大 APK 体积。跨版本升级注意字段兼容。旧版 Apktool 生成的apktool.yml可能缺少compactEntries、featureFlags等新字段加载时这些字段为 nullgetter 有默认值兜底一般能正常重建但若您在极旧文件上手工添加新字段建议先用当前版本重新 decode 一遍让工具自己重写档案。写在最后apktool.yml是反编译目录的重建蓝图由 ApkInfo 类以 YamlSerializable 协议生成与读取。解码时它被填充与落盘编码时被ApkBuilder消费SDK、版本、资源、压缩策略全部以它为准。记住改 APK 前先读懂它的档案袋蓝图在手重建不愁。【免费下载链接】ApktoolA tool for reverse engineering Android apk files项目地址: https://gitcode.com/GitHub_Trending/ap/Apktool创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考