当前位置: 首页 > news >正文

读懂 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
resourcesInforesources.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),仅供参考

http://www.cnnetsun.cn/news/4159124.html

相关文章:

  • MusicPlayer2:免费开源的本地音乐播放器,10分钟讲透歌词、封面与音效
  • hubot-rocketchat架构深潜:hubot-meteorchat驱动模型与@rocket.chat/sdk响应式订阅完整指南
  • 一文掌握grafar.map:响应式依赖追踪与自动拓扑推断完全教程
  • 如何快速上手XRNeRF:Neural Radiance Field环境部署完整指南(含Docker一键搭建与避坑清单)
  • 122、双摄/多摄标定——外参标定与视差校正的产线实现与精度控制
  • 如何为Service Worker写测试?serviceworker-rails完整测试套件逐例解析
  • 告别误删生产集群:zsh-kubectl-prompt最佳实践清单与K8s终端效率提升总结
  • 三天实战阿里云:从零搭建高可用Web应用架构
  • Rollbar.js Telemetry 遥测深度解析:用『面包屑』快速还原用户报错现场的完整指南
  • 5 分钟快速上手 D-Zone:从零部署 Discord 活动模拟器的新手入门教程
  • hubot-rocketchat安全实践:bot用户权限管控、私有频道隔离与HTTPS证书连接避坑
  • Siberite性能基准测试实战:复现70K QPS洪峰测试并与Kestrel、Darner内存占用对比
  • XUnity.AutoTranslator:Unity 游戏自动翻译,装好改 3 行配置就能跑
  • cloud189-auto-save AI 功能实战:3步玩转智能重命名、自动过滤与季文件夹标准化
  • 单片机开发工具篇:(七)Keil MDK 如何更改已有工程的工程名
  • Direct3DHook 屏幕捕获高级技巧:多重采样 Resolve、GPU 缩放与 KeyedMutex 跨设备纹理共享全解析
  • 误改配置别慌!Envault变量版本历史与一键RollBack回滚功能深度解析
  • ComfyUI 节点工作流怎么用:从0到跑通第一个图生视频的5个步骤
  • Normy乐观更新实战:用optimisticData与rollbackData实现UI即时响应和自动回滚
  • 像素级渲染生成色彩图:Colorful ColorMapView颜色轮实现原理深度解析
  • 零依赖的传奇:10年仍流畅运行的纯JS复古贪吃蛇游戏——JavaScript-Snake 项目全览
  • MMAS Money Tracker 支出分析指南:用饼图一眼看穿你的钱花在了哪里
  • Megatron-LLM快速上手:手把手微调LLaMa 2 7B的完整实战教程(含500M tokens代码数据)
  • pg_pathman内置监控视图清单:pathman_partition_list等4大视图一键掌握分区状态
  • Lightbeam的心脏:一个HTTP请求如何从Firefox事件捕获变成图谱上的一个节点
  • 为什么 QMedia 的多模态 RAG 问答更聪明?图文短视频内容检索与智能问答使用详解
  • 一行use解决:Hammox.Protect宏如何让Elixir测试模块自带契约检查
  • heroku_san安装教程:Rails 3+、Rails 2与Sinatra三种框架如何快速接入Heroku部署
  • PyMacroRecord:免费跨平台宏录制工具,重复操作一键回放
  • 深入 Duster 架构:基于 Laravel Zero 与适配器模式统一 4 大 Lint 工具的设计之道