资讯动态

APK逆向资源提取:apktool解包回编译与签名闭环

发布时间:2026/10/28 2:36:37 来源:尧图企业网站定制
简介面向 Android 逆向初学者以及需要在 macOS 上提取 APK 资源的开发者这套工具集围绕 apktool 这一经典的反编译与重打包工具展开帮助读者解决 Mac 环境下配置运行环境、解包 APK 并取出图片、布局、清单等原始资源的实际问题。压缩包共收录 3 个文件由 1 个 jar 核心程序、1 个 sh 启动脚本和 1 份 md 说明文档组成整体约 10.25MB体量轻便下载后按说明即可完成环境准备与命令调用无需再自行拼装零散组件。说明文档对工具用途、参数含义与常见操作流程作了梳理配合脚本能减少手动输入命令时的出错概率遇到异常时也可从文档中找到排查方向。目前已有 2832 人学习下载适合刚接触 APK 逆向、希望快速跑通第一条解包命令的读者作为上手材料也可作为日常提取应用资源时的常备工具集。1. 找回旧项目图标资源的那一次经历几年前接手一个没人维护的旧客户端设计源文件全丢了唯一还在的是测试机上装着的那个安装包。为了把图标、配色和字符串捞回来我试过直接改后缀解压得到的是一份二进制 AndroidManifest 和一堆没有名字的 res 目录也试过只反编译代码的工具逻辑能看资源依旧是一团索引。最后是 apktool 一条 decode 命令收场清单变成可读文本resources.arsc 被拆成 values 下的 xml图标按密度分好文件夹肉眼就能挑。这篇讲的就是 apk 逆向里最基础也最容易被低估的那一步——用 apktool 稳定地获取 apk 资源再把改过的东西编译、签名、装回设备跑成闭环。适合需要从自有或已授权安装包里捞资源的人也适合刚碰逆向、被报错卡在半路的人。2. 工具选型apktool、jadx、aapt2、unzip 各自该站哪个位置2.1 四种“解包”方式的本质差别很多人把“解 APK”当成一件事其实四个工具干的是四件完全不同的事混着用就会反复翻车。unzip 只是解开 zip 容器。它能拿到 res 目录下的原始文件、assets、lib 下的 .so、classes.dex但 AndroidManifest.xml 是二进制 AXMLresources.arsc 是二进制资源表.9.png 是带 chunk 的图片。你打开看到的是一堆乱码和奇怪文件名原因是这些文件本来就不是给文本编辑器看的。aapt2 是只读的检查器。aapt2 dump badging能秒出包名、版本号、权限、支持的密度和语言aapt2 dump resources能打出完整的资源 ID 表。它的价值在于“确认”不在于“产出”跑得飞快适合做交叉验证。jadx 处理的是 dex。它把字节码还原成接近 Java 的代码逻辑分析靠它。但它的资源输出是副产物目录结构和命名还原得不如 apktool 完整想改 strings.xml 再编回去基本指望不上。apktool 做的是“可逆”这件事。它反编译清单和资源把它们还原成能读能改的 xml 和图片同时生成 apktool.yml 记录包名、版本、minSdk、资源 ID 偏移等元信息让回编译有据可依。这是它区别于前三个工具的核心——它不只让你看还让你改完再装回去。工具处理对象输出形态能否回编译典型用途unzipzip 容器原始二进制文件否快速取 assets、字体、soaapt2清单与资源表文本报告否查包名、权限、资源 IDjadxdex 字节码Java 风格代码否需另行处理逻辑分析、协议定位apktool清单 资源 dex可编辑 xml / smali是取资源、改配置、回编译判断依据很清楚只要你的目标里带“改完还要能用”apktool 就是唯一选项如果只是看一眼用轻的那个别为了查个版本号去跑一遍完整反编译。2.2 用场景反推工具五种常见需求对应哪种选择我一般会先问清楚要干什么再决定用哪个顺序错了会白花几十分钟。第一种只想拿图标、字体、音效这些原始文件。这类需求根本不需要反编译改后缀解压或者apktool d -s跳过 dex几十秒就完事。拿图标时注意 drawable 下按 dpi 分了五六套别把 mdpi 的那份当成唯一版本设计稿对不上多半是挑错了密度。第二种要改应用名、默认语言、颜色值、启动图再装回设备。这就必须走 apktool 全套decode、改 res/values、build、zipalign、签名。中间任何一步偷懒最后都会卡在安装失败上。第三种只想看某段逻辑怎么写的。直接上 jadx别用 apktool 反编译出 smali 再硬啃除非你要改的就是 dex 层。第四种想确认一个包到底声明了什么权限、targetSdk 多少、有没有某条 feature。aapt2 dump badging 一条命令比解包快一个数量级适合批量比对。第五种两个版本的包做资源差异对比。用 apktool 各解一份到不同目录diff -r跑一遍字符串和布局的增删一目了然。这个用法在排查“为什么新版本某个页面文案变了”的时候特别省事。2.3 环境准备Java、路径、内存这三个变量先定下来apktool 本体是个 jar外面包了层启动脚本所以第一件事是 Java 环境。2.x 系列对 Java 8 及以上都能跑但同一台机器上装了多个 JDK 的时候脚本抓到哪个不确定先确认版本再动手。# 确认当前生效的 java 版本apktool 2.x 建议 Java 8 及以上 java -version # 确认 apktool 脚本能找到 jar正常会打印版本号 apktool -version # 内存不够时给 JVM 直接下参数大包解包 OOM 多半是这里没设 apktool -J-Xmx4g d app-release.apk -o out_dir第二件事是路径。Windows 上目录名带中文、带空格、带括号是 build 阶段一堆莫名其妙的“file not found”的常见来源。我习惯在工作盘根目录建一个纯英文的 work 目录所有解包产物都放里面路径最短问题最少。第三件事是版本统一。同一个 APK用不同版本的 apktool 解出来的 smali 和 public.xml 可能有细微差异回编译的兼容性也不一样。团队里几个人一起看同一个包先对一下版本号不然你改完能编过、他改完编不过排查方向会跑偏。这条是血泪经验不是洁癖。3. 一次完整的 decode从 apk 到可读资源目录3.1 最小可用命令以及每个参数为什么存在最短的一条命令就够跑通但参数得知道含义不然出了错不知道从哪调。# 最基础的解包-o 指定输出目录-f 表示目录已存在时强制覆盖 apktool d app-release.apk -o out_dir -fd是 decode 子命令b是 buildif是 install-framework。三个子命令覆盖了绝大部分场景。-o后面跟输出目录。不写的话 apktool 会在当前目录建一个以 apk 文件名命名的文件夹包名带版本号的时候会生成一长串名字回头自己都找不到所以我基本都会显式指定。-f是强制覆盖。第二次解同一个包必须加否则它会停在那里等确认脚本化的时候直接卡死。-s等价于--no-src跳过 dex 反编译。取资源场景必加速度快好几倍产物里也不会多出几千个 smali 文件。-r等价于--no-res反过来不解资源只解 dex。这个用在只想看 smali 的时候比如做加固分析或者指令级对比。-p指定框架目录-t指定框架标签。这两个参数平时用不到但解厂商定制的系统应用时会救命——那些包引用了大量私有属性不装对应框架回编译一定报属性找不到。-c等价于--copy-original保留原始的 AndroidManifest.xml 和 resources.arsc 副本到 original 目录。我习惯加上相当于给自己留个后悔药改坏了能直接对照原样。--only-main-classes只反编译主 dex多 dex 的大包用它能把时间压下来一大截代价是看不到 secondary dex 里的类看清楚需求再决定。参数表整理一下更直观参数全称作用建议-o--output指定输出目录总是显式指定-f--force覆盖已存在目录脚本里必加-s--no-src跳过 dex 反编译只取资源时必加-r--no-res跳过资源反编译只看逻辑时用-c--copy-original保留原始清单与资源表建议默认加-p / -t框架路径 / 标签加载厂商框架系统应用才需要3.2 解出来的目录怎么读哪些能改、哪些别碰跑完之后目录大致是这些内容每个的位置和风险等级都不一样。AndroidManifest.xml 是还原后的可读清单包名、权限、Activity 声明都在里面可以改改之前先备份。apktool.yml 是 apktool 自己的元信息文件记录版本、minSdk、资源 ID 起始值、是否用了 aapt2 等。这个文件不要手改改错一位回编译出来的资源 ID 就整体偏移装上去要么闪退要么资源串位。res 目录是重点。values 下的 strings.xml、colors.xml、styles.xml、dimens.xml 是纯文本改文案改颜色都在这里layout 下是布局 xmldrawable 系列是图片注意密度目录的对应关系。smali 目录是 dex 反编译出来的字节码一个类一个文件加-s就不会生成。要看逻辑用 jadx 更舒服但要改 dex 层只能在这动。lib 目录放 .soassets 放原始资源这两个基本都是原样拷贝改动风险低但也没什么可改的。original 目录是-c的产物存着原始的二进制清单和资源表出问题时用来对照。build 和 dist 是编译阶段的产物目录解包时可能不存在build 之后 dist 下默认会出现回编译的 apk。有一类文件特别要提醒res/values/public.xml。它是资源 ID 的映射表编译时按顺序分配 ID。删掉其中一条后面的 ID 全部前移运行时按旧 ID 找资源的代码就会指到别的地方。要删资源正确做法是删文件本体再重新编译而不是去动 public.xml 的条目。提示动手改之前先对 out_dir 做一次版本控制git init git add -A git commit三行命令改坏了随时回滚比任何备份策略都省心。3.3 只取资源不碰代码-s 与资源筛选的实操大部分“获取 apk 资源”的需求其实不需要 dex这时候命令应该长这样# 只解资源跳过 dex 反编译速度快且产物干净 apktool d app-release.apk -o res_only -s -f -c # 只想要原始图片和字体不做 xml 还原直接解压更快 unzip -o app-release.apk res/* assets/* -d raw_res第一条适合要改 xml 的场景第二条适合只要二进制原件的场景。两者差别在于apktool 会把 .9.png 还原成带边框信息的 png 并单独处理而 unzip 拿到的是编译后的成品能直接用于非 Android 环境比如把图标挪到网页项目里。如果目标更窄比如只要某一类资源解完之后按后缀筛就行# 找出所有 svg 和 webp快速判断图标资源规模 find res_only/res -type f \( -name *.svg -o -name *.webp \) | wc -l # 只保留 values 下的字符串用于文案对照 ls res_only/res/values*/strings.xml这里有个容易被忽略的点values后面可能带语言后缀values-en、values-zh-rCN都是独立目录默认语言的 strings.xml 在values里其他语言的同名 key 覆盖它。想把某个语言下的文案全捞出来得把这些目录一起看只看 values 会漏掉大半。还有一种场景是“只想确认某个资源存不存在”。这时候不用全解aapt2 dump resources app.apk | grep 关键词一条命令就有答案再决定要不要解包。先确认再动手能省掉不少无谓的解包。4. 重编译与签名让改动真正生效的完整流程4.1 build 阶段的参数与报错定位顺序改完 res 之后回到编译这一步。# 回编译-o 指定输出的未签名 apk--use-aapt2 走新版资源编译器 apktool b out_dir -o unsigned.apk --use-aapt2b是 build第一个位置参数是解包目录-o是输出文件名。不加-o的话产物默认落在out_dir/dist/下文件名带包名和版本容易找错。--use-aapt2建议加上。aapt2 对资源引用的检查更严格也更接近现代构建链的行为很多在 aapt 下能蒙混过关的错误用 aapt2 会在编译期暴露早暴露比装到设备上闪退强。-c在 build 阶段同样可用表示原样拷贝那部分不做处理的内容配合解包时的-c使用。报错的时候不要从头翻先看 stderr 的第一处错误。apktool 的错误往往是连锁的第一条才是根因后面的都是它引发的次生灾害。常见的第一条长这样W: error: resource attr/xxxColor not found.这行的意思是某个 xml 引用了 attr/xxxColor但资源表里没有这个属性。往上一层找通常是两种原因一是这个属性来自厂商框架需要apktool if framework-res.apk装框架二是这个 xml 是别的应用里拷过来的属性根本没跟着来。前者装框架后者删引用二选一不要试图硬改资源表。还有一类错误长这样I: Building resources... W: .../res/values/styles.xml:12: error: Error: No resource found that matches the given name带文件路径和行号的都好办直接跳过去看那一行。真正难办的是不带行号的通常是 public.xml 与 res 目录不一致导致的回头检查是不是手工删过文件。4.2 zipalign 与 apksigner顺序错了等于白干编译出来的 unsigned.apk 不能直接装必须对齐加签名。这两步的顺序是死的颠倒过来签名就废了。# 第一步4 字节对齐-p 保证页对齐顺序必须在签名之前 zipalign -p -f 4 unsigned.apk aligned.apk # 第二步生成一个自用签名只做本机测试不要用于任何对外分发 keytool -genkeypair -v -keystore demo.jks -keyalg RSA -keysize 2048 \ -validity 3650 -alias demo -dname CNdemo, OUdev, Odev, Ldev, STdev, CCN # 第三步签名v1/v2/v3 由 apksigner 按 minSdk 自动选择 apksigner sign --ks demo.jks --ks-key-alias demo --out signed.apk aligned.apk # 第四步验证签名是否真的生效 apksigner verify -v --print-certs signed.apk # 第五步装机验证 adb install -r signed.apk为什么顺序不能反apksigner 会把签名块写进 zip 的特定位置之后再做 zipalign 会移动文件偏移签名校验直接失败表现为安装时报“签名不一致”或者解析包错误。很多人卡在这一步反复重签都没用就是顺序错了。为什么必须签名Android 从早期版本起就拒绝安装未签名的包而且升级安装要求签名一致。用调试签名覆盖原应用的正式签名只能先卸载再装数据会丢这一点在做“改完验证”的时候要提前想清楚别把测试机上的数据弄没了。签名方案的选择不用手调。apksigner 会根据 APK 的 minSdk 自动决定要不要带 v1JAR 签名minSdk 低的包会同时带 v1 和 v2保证老设备能装。手工指定反而容易出问题。还有一个容易忽略的细节targetSdk 较高的应用resources.arsc 在 APK 里必须是不压缩且 4 字节对齐的。apktool 回编译时会处理好但如果你中途用别的工具重新打包过 zip这个约束就被破坏了表现是安装成功但启动即崩logcat 里能看到资源加载相关的异常。所以整条链路最好一次跑完不要中途换工具。4.3 装机前的自检清单每次改完我都会按下面这张表过一遍五分钟能省掉半小时的反复。检查项怎么查不通过的典型表现编译是否零错误看 build 输出有没有 W/error产物缺失或大小异常是否已对齐zipalign -c -v 4 signed.apk安装报解析包错误签名是否有效apksigner verify -v安装报签名不一致包名版本是否被改aapt2 dump badging signed.apk覆盖安装失败或装成两个minSdk 是否被抬高同上看 sdkVersion老设备装不上资源是否串位打开应用看图标文案图标错乱、文案对不上最省事的做法是把这几条写成脚本编译完直接跑输出一屏结论。人眼检查五条第三次就会漏掉一条这不是态度问题是必然。5. 排查手册apk 逆向取资源时最容易翻车的 5 个点5.1 现象build 报 “No resource identifier found for attribute”现象很明确编译到一半停住提示某个属性在包里找不到通常出现在 styles.xml 或 layout 里。原因是这个属性不属于标准 Android 框架。常见于从厂商系统应用里拷出来的布局或者从别的定制 ROM 里扒下来的资源它们引用了私有 attr。apktool 加载的是标准框架自然找不到。解决分两条路。如果这个包本身就是系统应用先装对应框架apktool if framework-res.apk -t 厂商标签然后解包时用-t指定同一个标签属性就能解析。如果不是系统应用说明这段资源是从别处搬来的直接删掉那几行引用或者换成标准属性。别去手写 attr 定义糊弄编译器编过了运行时也会因为资源缺失崩掉。5.2 现象编译通过安装时报“解析包出现问题”这是最气人的一类前面全绿最后一步死。原因基本只有三种没签名、签名后又被 zipalign 动过、或者手工改过 zip 里的 resources.arsc 导致对齐被破坏。第一种最常见apktool build 出来的产物名字就叫 unsigned很容易忘。解决顺序是先apksigner verify -v看签名状态再zipalign -c -v 4看对齐。两个都过了还装不上检查是不是覆盖安装了签名不同的包卸载重装试试。如果卸载能装、覆盖不能装那就是签名不一致没有别的解释。5.3 现象中文变成 \u 开头的转义或者直接乱码改完 strings.xml 一编译界面上出现一堆\u4f60\u597d这样的字符。原因是 Android 的字符串资源里非 ASCII 字符用\uXXXX转义是合法且常见的写法apktool 反编译时会保留原始转义形式。你用编辑器直接敲中文进去如果文件编码不是 UTF-8或者 XML 声明里的 encoding 与实际不符就会两头不讨好。解决办法是统一编码文件保存为 UTF-8 无 BOMXML 头部声明保持encodingutf-8中文直接写或者写成\uXXXX都行但一个文件里别混着来。遇到已经是转义的内容别手动一个个替换用脚本批量转# 把 strings.xml 里的 \uXXXX 转义批量还原成中文便于对照翻译 import re, pathlib def unescape(m): return chr(int(m.group(1), 16)) for p in pathlib.Path(out_dir/res).rglob(strings.xml): text p.read_text(encodingutf-8) new re.sub(r\\u([0-9a-fA-F]{4}), unescape, text) if new ! text: p.write_text(new, encodingutf-8) print(rewritten:, p)跑之前记得目录已经纳入版本控制转错了能退回去。另外带占位符的字符串%1$s、%d不要在转换过程中被破坏正则只匹配\u加四位十六进制不会误伤。5.4 现象9-patch 图重编译后拉伸错乱改了一张 .9.png 的背景图装上去发现边框被拉成一条黑线或者圆角糊掉。原因是 .9.png 不是普通 png它四周各有 1 像素的透明边框里面用纯黑像素标记可拉伸区域和内容区。apktool 解包时会把这张图还原成带标记的可编辑形式你直接拿图像工具改中间区域还安全一旦用工具重新导出、或者顺手裁掉了一圈边框标记就没了编译时按普通 png 处理拉伸自然失控。解决办法是改图时只动中间的内容区别碰最外一圈像素用不支持保留边框的工具处理过就得重新画标记线。实在懒得处理解包时加-c保留原始副本改完直接把原始的那张拷回去用原图不改问题自然消失。这也是我为什么建议默认带上-c。5.5 现象大包解包直接 OOM 或长时间卡住解一个几百兆的包进度条走到 smali 阶段Java 抛 OutOfMemoryError或者风扇狂转十分钟没动静。原因是默认堆内存不够而 dex 反编译是全文加载再逐类处理的包越大吃得越多多 dex 应用的 secondary dex 更是雪上加霜。解决办法是给 JVM 直接下参数apktool -J-Xmx4g d ...机器内存够就往上加。同时用-s跳过 dex只解资源时间能从十分钟压到几十秒。如果确实需要 smali再加--only-main-classes只处理主 dex先看主 dex 够不够用不够再单独解。顺带一提路径里带中文或空格在大包场景下更容易触发奇怪的 IO 异常统一挪到纯英文短路径能排掉一类玄学问题。6. 收尾一套能直接抄的流程和一个习惯把上面这些串起来我现在的固定动作是这样几乎不再临场想参数# 1. 建工作目录并纳入版本控制改动全程可回滚 mkdir -p work/app cd work/app git init # 2. 只解资源保留原始副本强制覆盖 apktool d app-release.apk -o src -s -f -c # 3. 提交基线之后每一次改动都有对照 git add -A git commit -m baseline # 4. 改完回编译走 aapt2 apktool b src -o unsigned.apk --use-aapt2 # 5. 对齐 签名 验证一条链跑完不中断 zipalign -p -f 4 unsigned.apk aligned.apk apksigner sign --ks demo.jks --ks-key-alias demo --out signed.apk aligned.apk apksigner verify -v --print-certs signed.apk这套流程里最值钱的不是哪条命令而是第 3 步。我第一次大规模改资源的时候没做基线改到一半发现某个 values 文件被编辑器自动格式化缩进全变diff 出来几千行根本看不出自己动了什么最后只能重新解一遍从头再来。从那以后我每次动手前都强制先 commit 一份干净基线改一个模块提一次出问题直接 checkout 回上一个节点比任何备份策略都省事。再补一个判断依据帮你决定要不要往下走如果目标只是拿到图标和文案做到第 2 步就够了后面的编译签名完全是多余动作只有当你需要“改完还要装回设备看效果”时才值得把整条链跑通。工具链的价值不在于用满而在于用到刚好。最后提一句边界。这套东西我只用在自己开发的包、明确授权的测试包以及公开分发的开源应用上改完的产物自己验证完就扔不二次分发也不去碰任何绕过付费和验证的路子。技术上能做到和应该做是两件事分清楚能省掉很多麻烦。工具和配套的说明我整理成了一份包需要的时候取来对着跑一遍就行。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑