尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Apktool ApkInfo 全解:apktool.yml 是怎么被读写并支撑重打包的

Apktool ApkInfo 全解:apktool.yml 是怎么被读写并支撑重打包的 Apktool ApkInfo 全解apktool.yml 是怎么被读写并支撑重打包的【免费下载链接】ApktoolA tool for reverse engineering Android apk files项目地址: https://gitcode.com/GitHub_Trending/ap/Apktool用 Apktool 反编译一个 APK输出目录里会生成apktool.yml重新打包时又靠这个文件决定怎么构建。可这些字段到底是谁写进去的、构建时又是谁在消费答案是 meta 包下的 ApkInfo 类——Apktool 中 APK 元数据的唯一载体贯穿解码与重打包两个阶段。整体架构一个对象、一套自研 YAML、两个消费方ApkInfo 处在两个环节的交接点上数据链长这样APK ── ApkDecoder解码── ApkInfo 对象 ──save()── apktool.yml apktool.yml ──ApkInfo.load()── ApkInfo 对象 ── ApkBuilder重打包拆开看是三层元数据层ApkInfo 及 meta 包下的五个子对象UsesFramework、SdkInfo、VersionInfo、ResourcesInfo外加 usesLibrary 与 doNotCompress 两个列表序列化层brut.j.yaml 模块里自研的 YamlReader/YamlWriter不依赖第三方 YAML 库逐行解析消费方ApkDecoder 负责填充并落盘ApkBuilder 负责读回并消费简单说apktool.yml是解码与重打包之间唯一的交接契约ApkInfo 是这份契约唯一的翻译器。核心流程从解码到重打包走一遍解码侧ApkDecoder 边解边填最后落盘在 ApkDecoder 里能看到完整的写入链路mApkInfo new ApkInfo(); mApkInfo.setVersion(mConfig.getVersion()); mApkInfo.setApkFile(mApkFile); mResDecoder new ResDecoder(mApkInfo, mConfig); // ... 资源、Manifest 解码完成后 ... mApkInfo.save(outDir);关注最后一行save() 就是打开输出目录下的固定文件名apktool.yml调用 write()。SDK 版本、版本号、资源包 ID 这些字段并非都直接由 ApkDecoder 填而是解码资源与 Manifest 的阶段回写进同一个对象featureFlags、doNotCompress 条目也是这一过程陆续追加的。重打包侧ApkBuilder 读回来立刻用ApkBuilder 的第一步正好是上面链路的反向操作mApkInfo ApkInfo.load(mApkDir); String minSdkVersion mApkInfo.getSdkInfo().getMinSdkVersion(); mSmaliBuilder new SmaliBuilder(minSdkVersion ! null ? SdkInfo.parseSdkInt(minSdkVersion) : 0); mAaptInvoker new AaptInvoker(mApkInfo, mConfig);关键在第二行起minSdkVersion 决定 smali 汇编的输出等级AaptInvoker 则拿 apkInfo 里的框架与资源信息决定怎么编译资源。而apktool.yml一旦缺失load() 会抛 IOException 并被包装成 AndrolibException——这就是没有 apktool.yml 就构建不了的直接原因。序列化层逐行解析而不是 YAML 库YamlReader 在构造时把输入流切成一行行 YamlLine每行只记四个属性indent空格数、hasColon、isItem-开头、isComment。readRoot 只处理缩进为 0 且带冒号的行再分发给 readItem()sdkInfo 这类嵌套对象由 readObject 读取要求数据行缩进严格大于键行。这个设计带来一个直接后果无法识别的行——未知字段、缩进不符——会被静默跳过而不是报错。apktool.yml 里每个字段到底存了什么看仓库测试资源里的真实样例testapp/apktool.yml 前段version: 2.3.2 apkFileName: testapp.apk usesFramework: ids: - 1 versionInfo: versionCode: 1 versionName: 1.0 resourcesInfo: packageId: 127 sparseEntries: false # ... 其余字段省略 ... doNotCompress: - arsc - png各字段与 ApkInfo 的成员变量一一对应几个容易忽略的点空字段干脆不写write() 里每个字段都有非空判断所以某个文件里看不到 sdkInfo 属正常现象-1 与 nullversionCode 为 -1、versionName 为 null 表示原 Manifest 没声明版本YamlWriter 会把 null 写成字符串null读回时再还原packageId 通常为 1270x7fAPK 里的资源引用都依赖这个值改动它会导致引用错位sdkInfo 存的是字符串parseSdkInt 还认识 O、TIRAMISU 这类发布名别名getTargetSdkVersionBounded() 会把 target 钳到 [min, max] 区间内apkFileName 有加载校验值为./..或含路径分隔符会直接抛 SecurityException这是对 CVE-2022-0476 的防御典型用法查框架、批量改元数据、控压缩如果你要快速确认 APK 依赖的框架看usesFramework.ids1 是默认 Android 框架tag 有值时指向自定义框架标签如果你要在脚本里批量改元数据load → 修改 SdkInfo/VersionInfo → save() 即可仓库里的 ApkInfoSerializationTest 做的就是这种 load → save → 再 load 的往返断言可当模板参考如果你要控制重打包时的文件压缩doNotCompress 决定哪些文件在新 APK 中以未压缩方式存放解码器会自动往这里记录 0 字节文件和不常规扩展名的文件别手删条目常见坑缩进丢数据、SecurityException、解析异常缩进错误不报错只丢数据。现象改了 apktool.yml构建行为却像没改。原因readRoot 会静默跳过缩进不符或没有冒号的行未知字段同样被忽略。解法手工改完后用 ApkInfo.load() 读回并断言关键字段ApkInfoReaderTest 的 testSkipIncorrectIndent 就是这么验证的。apkFileName 写成路径直接 SecurityException。现象抛 Malicious value for apkFileName 而非解析错误。原因加载时校验拒绝./..及含/、\的值。解法该字段只记 APK 原始文件名别带路径。sdkInfo 手写真数导致解析异常。现象构建失败NumberFormatException。原因值按字符串读入parseSdkInt 先试发布名别名、回退 Integer.parseInt对任意写法并不宽容。解法写数字如 25或合法别名如 TIRAMISU。删掉 apktool.yml重打包立刻失败。现象apktool b报 FileNotFoundException。原因ApkBuilder 第一步就是 ApkInfo.load(mApkDir)它是构建参数的唯一来源。解法把它当作反编译目录的一部分提交进版本库。apktool.yml是解码与重打包两个阶段之间的交接契约ApkInfo 是这份契约唯一的翻译器。看懂这个文件构建为什么长这样就不再是黑盒。【免费下载链接】ApktoolA tool for reverse engineering Android apk files项目地址: https://gitcode.com/GitHub_Trending/ap/Apktool创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表