简介:这份资源面向需要在 macOS 环境下进行 APK 逆向与资源提取的开发者与安全学习者,提供了一套可直接上手的 apktool 工具集合,帮助解决反编译、解包与获取 APK 内部资源等常见需求。压缩包共 3 个文件,以 jar 主程序、sh 启动脚本和 md 使用说明为主,整体约 10.25MB,体积轻便,解压后即可按说明配置运行,无需额外寻找依赖。目前已有 2829 人学习下载,说明该工具组合在同类场景中具备一定参考价值。使用说明文档对命令参数、常见报错与操作流程做了整理,配合脚本可减少手动配置成本,适合刚接触 APK 逆向的读者快速跑通流程,也方便有经验的开发者将其作为日常反编译与资源提取的辅助工具,遇到问题还可通过留言交流排错思路。
1. 拆开一个 APK 到底能看到什么:从“黑匣子”到可读资源
很多人第一次拿到一个 APK,第一反应是把它当压缩包改后缀解压,结果发现AndroidManifest.xml打开是乱码,resources.arsc根本读不了,图片倒是能翻出来几张,但布局文件全是二进制。这就是 APK 逆向里最典型的“黑匣子”时刻——你以为解压就完事了,实际上 Android 在打包时把大量资源编译成了二进制格式,不经过专门工具处理,你看到的只是一堆无法直接阅读的字节。
apktool 就是干这个的。它能把 APK 里的二进制 XML、资源索引表、DEX 之外的资源文件还原成接近源码结构的可读格式,同时还能在修改之后重新打包回 APK。对于做 APK 逆向、获取 APK 资源、分析竞品实现、汉化或二次修改的人来说,这是绕不开的基础工具。它适合谁?适合需要看别人 APK 里布局怎么写的、想提取原始图片和字符串资源的、要改个图标或文案再打回去的从业者。如果你只是想在手机上装个应用,那用不上它;但如果你要“拆开看”,它就是第一把螺丝刀。
2. apktool 环境搭建与反编译全流程:从零到拿到可读资源
2.1 为什么不用普通解压而用 apktool
普通解压工具(比如把.apk改成.zip再解压)能拿到classes.dex、res/目录下的部分原始图片,但AndroidManifest.xml和res/layout/下的 XML 全是二进制格式,resources.arsc更是编译后的资源索引表。apktool 的核心价值在于它内置了 AXML 解码器和 ARSC 解析器,能把二进制 XML 还原成带标签、带属性的文本,把资源索引表还原成可读的res/values/strings.xml、public.xml等文件。换句话说,普通解压给你的是“原材料”,apktool 给你的是“图纸”。
另一个关键点是 apktool 支持回编译。你改了res/values/strings.xml里的文案,或者换了res/drawable/下的图标,apktool 能重新打包成 APK,并且处理签名对齐。普通解压改完文件再压回去,大概率装不上,因为资源索引和二进制 XML 的格式对不上。
2.2 安装与版本选择
apktool 是 Java 写的,所以第一步是确认 Java 环境。我一般用 JDK 8 或 JDK 11,太高版本偶尔会遇到兼容性玄学。检查命令:
java -version如果输出1.8.0_xxx或11.0.x就够用。然后去 apktool 官方仓库下载两个文件:apktool.jar和对应的包装脚本(Windows 是apktool.bat,Linux/macOS 是apktool)。把apktool.jar和脚本放同一个目录,加到 PATH 里,或者直接在当前目录用java -jar apktool.jar调用。
注意:网上有些打包好的“一键版”会夹带旧版 jar 或修改过的脚本,建议直接从官方仓库拿最新稳定版。版本号不写死,因为更新频率不低,下载页会标。
验证安装:
apktool --version能输出版本号就说明环境通了。
2.3 反编译一个 APK:命令与参数拆解
假设你拿到一个target.apk,放在work/目录下。最基础的反编译命令:
apktool d target.apk -o output_dird是 decode 的缩写,-o指定输出目录。执行完之后,output_dir里会出现:
AndroidManifest.xml:可读的清单文件,能看到权限、组件、包名res/:资源目录,布局、图片、字符串都在这里smali/:DEX 反汇编出来的 smali 代码,按包名分目录apktool.yml:apktool 自己的元数据,回编译时要用
如果你只关心资源,不想看 smali,可以加-s跳过 smali 反汇编:
apktool d target.apk -o output_dir -s这样速度快很多,输出目录里没有smali/,但res/和AndroidManifest.xml照样完整。对于“获取 APK 资源”这个诉求,-s是常用选项。
还有一个参数-r,表示不反编译资源,只出 smali。这个用在只想看代码逻辑、不关心资源的场景。反过来,如果你只想要资源,-s就够了。
2.4 资源目录里到底有什么
反编译完成后,res/下的结构大致是:
| 目录 | 内容 | 说明 |
|---|---|---|
res/layout/ | 布局 XML | 已还原为可读文本,能看到控件层级 |
res/drawable/ | 图片资源 | 原始 PNG、WebP、XML 矢量图 |
res/values/ | 字符串、颜色、尺寸 | strings.xml、colors.xml、dimens.xml |
res/values/public.xml | 资源 ID 映射 | 每个资源的十六进制 ID 和名称对应关系 |
res/xml/ | 其他 XML 配置 | 网络配置、权限配置等 |
res/raw/ | 原始文件 | 音频、视频、数据库等未编译资源 |
public.xml是很多人忽略但很有用的文件。它列出了所有资源的 ID,如果你在做二次修改,需要知道某个资源的 ID 有没有冲突,这个文件就是参照。
2.5 回编译与签名:改完资源怎么装回去
改完res/values/strings.xml里的文案,或者替换了res/drawable/下的图标,下一步是回编译:
apktool b output_dir -o modified.apkb是 build,-o指定输出的 APK 文件名。这时候生成的 APK 是未签名的,直接安装会报INSTALL_PARSE_FAILED_NO_CERTIFICATES。需要签名:
keytool -genkey -v -keystore mykey.keystore -alias myalias -keyalg RSA -keysize 2048 -validity 10000这是生成一个自签名证书。然后可以用jarsigner或apksigner签名:
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore mykey.keystore modified.apk myalias或者用 Android SDK 里的apksigner:
apksigner sign --ks mykey.keystore --out signed.apk modified.apk签名之后才能安装到设备上。如果是做资源提取,这一步可以跳过;如果是做二次修改,签名是必经步骤。
3. 资源提取与修改的常见操作:字符串、图片、布局怎么动
3.1 提取字符串资源做多语言或文案分析
res/values/strings.xml是纯文本,直接打开就能看到所有硬编码的文案。如果你要做竞品文案分析,或者想批量提取关键词,可以用脚本处理:
import xml.etree.ElementTree as ET tree = ET.parse('output_dir/res/values/strings.xml') root = tree.getroot() for string in root.findall('string'): name = string.get('name') value = string.text print(f"{name}: {value}")这段代码把strings.xml里所有字符串的name和值打印出来。逻辑很简单:用ElementTree解析 XML,遍历string标签,取name属性和文本内容。参数上没什么需要调的,注意有些字符串可能包含占位符(比如%1$s),提取出来之后要保留原样,不要自己替换。
如果字符串在res/values-zh/strings.xml这种带语言限定符的目录里,说明有多语言资源,按目录分别处理就行。
3.2 图片资源提取与格式转换
res/drawable/下的图片通常是 PNG 或 WebP,直接复制出来就能用。但有些图片是.9.png(九宫格图),这种图带黑色边框标记拉伸区域,直接用会变形。处理方式是保留原文件,不要用普通图片编辑器重新保存,否则边框标记会丢。
还有一类是矢量图 XML,在res/drawable/下以.xml结尾。这种文件是VectorDrawable的定义,可以用 Android Studio 的 Vector Asset 工具导入,或者手动解析pathData提取路径。
批量提取图片的命令:
find output_dir/res -name "*.png" -o -name "*.webp" -o -name "*.jpg" | xargs -I {} cp {} ./extracted_images/这条命令把res/下所有图片复制到extracted_images/目录。find的-name参数可以按扩展名过滤,xargs配合cp做批量复制。注意如果文件名有空格,需要加-print0和xargs -0,不过 APK 资源文件名一般不会有空格。
3.3 布局文件分析与修改
res/layout/下的 XML 是还原后的布局文件,能看到LinearLayout、ConstraintLayout、TextView等控件层级。如果你要修改界面,比如隐藏某个按钮,可以找到对应的android:visibility属性改成gone:
<Button android:id="@+id/btn_submit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/submit" android:visibility="gone" />改完之后回编译、签名、安装。但要注意,如果这个按钮在 smali 代码里有逻辑引用,隐藏它可能导致空指针或逻辑异常。所以改布局之前,最好先确认这个控件在代码里有没有被findViewById引用。常见做法是先在 smali 里搜btn_submit这个 ID,看看有没有相关逻辑。
3.4 资源 ID 冲突与 public.xml 的作用
回编译时最常见的错误之一是资源 ID 冲突。apktool 在反编译时会生成public.xml,里面记录了每个资源的 ID。如果你新增了资源,或者修改了资源名称,可能导致 ID 重新分配,进而和 smali 里硬编码的 ID 对不上。
解决方式是尽量不新增资源,而是替换现有资源的内容。比如要换图标,直接覆盖同名文件,不要新建一个不同名的文件。如果必须新增,回编译时加--use-aapt2参数,让 apktool 用 AAPT2 处理资源,兼容性更好。
4. 避坑与排查:apktool 反编译回编译的五个血泪经验
4.1 反编译报错 “Could not decode file”
现象:执行apktool d时中途报错,提示某个资源文件无法解码,输出目录不完整。
原因:通常是 APK 使用了较新的资源格式,或者 apktool 版本过旧,不支持该 APK 的编译格式。也有可能是 APK 被加固或混淆过,资源被特殊处理。
解决:先升级 apktool 到最新版。如果还不行,尝试加--only-main-classes或-r跳过资源解码,先拿到 smali 再说。如果是加固 APK,apktool 本身处理不了,需要先脱壳,那是另一个话题。
4.2 回编译报错 “No resource found that matches the given name”
现象:apktool b时提示某个资源找不到,但明明在res/下看到了。
原因:通常是public.xml里的 ID 和实际资源对不上,或者资源文件名包含了大写字母或特殊字符,AAPT 不接受。
解决:检查报错提到的资源名,确认文件名是否合法(只允许小写字母、数字、下划线)。如果是public.xml的问题,可以尝试删除public.xml让 apktool 重新生成,但这样可能导致 smali 里的 ID 引用失效。更稳妥的方式是手动修正public.xml里的对应条目。
4.3 签名后安装报 “App not installed”
现象:回编译、签名都成功了,但安装到设备上提示“应用未安装”。
原因:常见原因有三个:签名用的证书和原 APK 不一致导致覆盖安装失败;APK 里的AndroidManifest.xml包名和原应用冲突;或者设备上已经装了同包名的应用,签名不同无法覆盖。
解决:先卸载设备上的原应用,再安装修改版。如果还是不行,检查AndroidManifest.xml里的package属性,确认包名没有意外改动。另外,有些应用有签名校验,修改后即使安装成功也可能闪退,这种情况需要处理签名校验逻辑,属于进阶操作。
4.4 反编译后 smali 目录为空
现象:apktool d执行完,res/和AndroidManifest.xml都有,但smali/目录是空的。
原因:可能是 APK 使用了多 DEX,而 apktool 默认只处理classes.dex,其他classes2.dex、classes3.dex没有反汇编。也可能是加了-s参数跳过了 smali。
解决:检查命令里有没有-s。如果没有,尝试加--force-manifest或手动指定 DEX 文件。对于多 DEX 的 APK,apktool 较新版本会自动处理,旧版本可能需要手动合并。
4.5 修改资源后应用闪退
现象:只改了一个字符串或一张图片,回编译安装后应用启动就闪退。
原因:可能是资源 ID 发生了变化,smali 代码里通过 ID 引用的资源找不到了。也可能是修改的图片格式不对,比如把.9.png用普通编辑器保存,丢失了九宫格信息。
解决:用logcat看崩溃日志,定位是哪个资源 ID 报错。如果是 ID 问题,对比修改前后的public.xml,看看 ID 有没有变。如果是图片问题,恢复原图,用专门的九宫格编辑工具处理。我一般改资源之前会先备份整个反编译目录,出问题直接回滚。
5. 进阶技巧:用脚本批量处理与验证回编译结果
5.1 批量反编译多个 APK 并提取资源
如果你手头有一批 APK 要做资源对比,手动一个个跑 apktool 太慢。可以写个 shell 脚本:
#!/bin/bash # 批量反编译当前目录下所有 apk,并提取 strings.xml for apk in *.apk; do name="${apk%.apk}" apktool d "$apk" -o "output_$name" -s if [ -f "output_$name/res/values/strings.xml" ]; then cp "output_$name/res/values/strings.xml" "strings_$name.xml" fi done这段脚本遍历当前目录所有.apk文件,用-s跳过 smali 加快速度,然后把每个 APK 的strings.xml复制出来单独保存。${apk%.apk}是去掉扩展名,用来命名输出目录。逻辑上没什么复杂,但批量处理时能省很多时间。注意如果 APK 文件名有空格,需要加引号处理。
5.2 验证回编译 APK 的完整性
回编译之后,怎么确认 APK 没坏?我一般用两个步骤验证。第一步,用aapt检查 APK 的基本信息:
aapt dump badging modified.apk这条命令会输出包名、版本号、权限、启动 Activity 等信息。如果输出正常,说明 APK 结构没大问题。第二步,用apksigner验证签名:
apksigner verify --verbose signed.apk输出里会显示签名是否有效、用了什么算法。如果这两步都过了,安装到设备上基本不会出问题。
5.3 资源对比:用 diff 找出修改前后的差异
如果你做了修改,想知道到底改了哪些文件,可以用diff对比反编译目录:
diff -r output_original/ output_modified/ > changes.txt-r表示递归对比子目录。changes.txt里会列出所有不同的文件。这个技巧在排查“为什么改完闪退”时特别有用,因为你能清楚看到哪些资源被动了。我一般会在修改前先复制一份原始反编译目录,改完之后 diff 一下,确认没有意外改动。
5.4 一个容易忽略的细节:apktool.yml 里的版本标记
apktool.yml里有一行version: x.x.x,这是 apktool 自己的版本标记,不是 APK 的版本。回编译时 apktool 会读这个文件,如果版本不匹配可能报错。如果你把反编译目录从一台机器拷到另一台机器,而两台机器的 apktool 版本不同,就可能出问题。解决方式是保持 apktool 版本一致,或者手动改apktool.yml里的版本号。这个坑我踩过一次,排查了半天才发现是版本标记的问题。
从那以后我每次反编译完都会先看一眼apktool.yml,确认版本和当前用的 apktool 对得上,再动手改资源。希望帮到你。
本文还有配套的精品资源,点击获取