
简介面向安卓开发者与逆向工程师的APK修改工具包集合了解析、资源编辑、反编译、编译、签名打包这一完整流程所需的一站式组件可用于深入查看APK内部结构、定制应用显示信息或移除内置广告。包内共186个文件整体仅8.76MB以94个smali字节码文件、45张png图片、13个xml配置文件为主同时包含jar/exe可执行工具、签名密钥、批处理脚本和测试APK覆盖从拆包、改资源、回编译到重新签名的各个操作环节。目前已有617人学习下载。借助这些组件读者可以练习修改应用标识与版本尾巴、调整界面文本和图片、分析并调整字节码逻辑还可以为修改后的安装包生成合法签名最终得到可正常安装的定制版本尤其适合刚接触逆向定制或希望在实战中摸清APK封装流程的入门与进阶读者。1. Android apk 修改工具的真相不是一键改包是一条工具链很多人以为「android apk 修改工具」是一个像 PS 一样点两下就出结果的软件双击 apk、改个图标、导出来就能装。真上手你会发现市面上所谓的一键改包工具多半只能应付「换图标、改包名」这一层一旦你想改布局、去广告、改接口地址或者干脆往 smali 里塞逻辑最后都得回到一条命令行工具链上apktool 负责解包与回编译jadx 把 dex 翻成可读的 Java 源码Android Studio 当校验器用。这篇笔记直接把我的操作过程、参数和踩过的坑摊开讲适合刚接触改包的新手也适合那些「改完签名总是装不上」的熟手翻翻排错思路。2. 环境与选型apktool、jadx、Android Studio 各负责什么改包这件事不是玄学它本质上是一套「逆向 → 修改 → 正向构建」的流水线。先把三个工具的分工和选型理由说清楚后面操作才不会一锅粥。2.1 工具链横评为什么不是某个国产一键工具我见过不少「改包神器」「apk 编辑器」的桌面工具界面友好、改图标确实快但边界非常明显只能处理未加固的包遇到加固壳腾讯乐固、360 加固直接打不开资源修改是黑盒改完后不知道改了哪些 ID回编译报错也没日志不支持 smali 级修改遇到需要改逻辑的场景束手无策签名固定用 v1/v2Android 14 及部分国产 ROM 上装不上。命令行工具链则把每一步拆开哪一步挂了都能看到真实报错。日常改包我用三件套加一个辅助工具职责选型理由apktool解包、回编译、改 smali/res/AndroidManifest唯一能保留资源目录结构并回编译的常规选择支持b命令反向构建jadx把 dex 反编译成 Java 源码方便定位逻辑比 jadx-gui 更适合在命令行里配合 grep快速找硬编码包名、接口地址Android StudioAPK Analyzer 查看最终包的目录、签名、DEX 结构不需要开整个工程直接拖 apk 进去就能看校验资源对齐和签名最直观aapt2 / zipalign / apksignerAndroid SDK 自带的资源编译、对齐、签名工具回编译后需要它们处理对齐和签名否则无法安装很多人会问「要不要用 Android Studio 编译成 apk」我的建议是日常改包不需要开一个完整工程去 buildAndroid Studio 只当一个「看包体检报告」的工具用。真要往 apk 里加 Java 逻辑那也是先写 smali而不是在 AS 里建工程——后者工作量完全是另一个数量级。2.2 环境准备Java 版本与基础命令apktool 是 Java 写的环境上最大的坑是 Java 版本。apktool 2.9.x 在 Java 11 下我跑得最稳Java 8 也能跑但 Java 17 偶尔会遇到反射报错属于已知兼容性问题。建议直接装 OpenJDK 11。java -version apktool -version验证完版本后进入第一个实操命令——解包。这里我用一个随手下载的普通未加固 apk 做示例把它解到一个干净的目录里。apktool d /path/to/app.apk -o /path/to/apk_out这条命令的参数含义d是 decode解包模式-o指定输出目录如果目录已存在且非空apktool 会停在那里问你是否覆盖不加-s时会把resources.arsc和 res 资源完整解码这是最常用的改资源模式如果只想改 smali、不想碰资源可以加-sno sources可以让反编译更快但资源修改就别想了。解包成功后目录里会出现smali文件夹Android 5.0 以下还可能有smali_classes2、smali_classes3对应多 dex、res文件夹、AndroidManifest.xml、apktool.yml。apktool.yml 里记录了解包时的框架版本和文件 hash回编译时它会拿这些做一致性检查。2.3 先摸清包体质判断能不能改解包之前最好先用 jadx 或 aapt 快速看一眼包的情况避免解完才发现是加固包白费时间。jadx --show-bad-code -d /path/to/jadx_out /path/to/app.apk grep -r com.tencent.legu\|com.qihoo.util\|bangcle /path/to/jadx_out --include*.java | head -5这里说明一下--show-bad-code让 jadx 在反编译中途遇到无法还原的代码块时尽量保留 smali别直接抛异常中断grep 搜的是常见加固壳特征类名腾讯乐固、360、梆梆都各有关键包名。搜不到特征类只能说明「没发现明显壳」不能 100% 断定可改最终还要解包后看AndroidManifest.xml里有没有service指向壳的加载器。这是一步成本很低的「体检」。我在一次把 360 加固包硬解包耗时半小时、回编译失败三次后养成了先 grep 的习惯——那半小时本来可以省下来去做脱壳选型。3. 第一轮实战改图标、改文案、替换资源文件工具链就绪现在进入真正的修改环节。这一步我会完整走一遍「改应用名 → 换图标 → 替换资源文件 → 回编译」的最小闭环命令和参数都直接可抄。3.1 解包后目录结构怎么看解包后的apk_out目录中优先关注三个地方res/values/strings.xml应用显示名、按钮文案、Toast 文本都在这res/mipmap-*/ic_launcher.png各密度下图标文件mdpi 是 48x48hdpi 72xhdpi 96xxhdpi 144xxxhdpi 192assets/游戏资源配置、远程配置 json、so 库之外的静态资源。3.2 修改应用名与替换图标先改字符串。打开res/values/strings.xml找到应用名直接改掉resources string nameapp_name改造后应用名/string /resources注意一处如果res/values-zh/下还有同名文件也要同步改否则中文系统上显示的还是旧名。这是新手最容易忽略的点values是默认语言兜底values-zh是中文覆盖。再看图标替换这是很多人觉得「换了个图标但桌面显示没变」的根源。cp /path/to/new_icon.png /path/to/apk_out/res/mipmap-xxxhdpi/ic_launcher.png不一定全部密度都换但至少要覆盖mipmap-xxxhdpi、mipmap-xxhdpi、mipmap-xhdpi、mipmap-hdpi四个密度。如果你只丢一张 192x192 的图小密度设备上会以缩放方式渲染视觉效果会有拉伸但绝大多数场景能用。替换完成后顺手做一次资源 sanity checkgrep -r 旧应用名 /path/to/apk_out/res/ | head这个检查是在全局 grep 旧应用名残留。名字不只是 xml 里有代码里也可能硬编码了窗口标题这一步发现后就得去 smali 里改下一节会展开。3.3 用 jadx 定位代码里的硬编码如果应用在启动后会把标题重新 setTitle光改 strings.xml 不够得在 Java 层面找到对应字符串。这一步用 jadx 反编译完的源码配合 grepgrep -r 旧应用名 /path/to/jadx_out --include*.java | head -10找到对应的R.string.app_name或硬编码字符串后回到 apktool 解出的 smali 目录里改。常见的位置是Landroid/app/Activity;-setTitle(Ljava/lang/CharSequence;)V附近的 const-string。注意 smali 里字符串是 UTF-8 常量标点符号别用全角半角混着写否则编译期能过、运行期乱码。3.4 替换 assets 资源与配置很多 app 把动态配置放在assets下比如游戏热更新地址、flutter 的assets/flutter_assets里的文本资源。这类文件没有编译期检查替换思路最简单cp /path/to/new_config.json /path/to/apk_out/assets/config.json唯一的坑是体积assets 里文件被压缩与否取决于apktool.yml里的doNotCompress配置。如果原包对某个后缀设置了不压缩你替换的文件体积发生大变某些 app 在运行时用AssetManager.openNonCompress()读取就会失败报 IOException。常见做法是先看apktool.ymldoNotCompress: - arsc - json - png里面出现扩展名必须保留针对新资源的处理如果原包没压缩某类文件替换后也应当不压缩apktool 回编译默认遵循这个配置不要手动去改否则容易踩Resources$NotFoundException的坑。4. 回编译与签名让改动后的 apk 真正能装进手机改完资源不是终点。apktool 解出来的东西是半成品必须回编译成可安装的 apk再经过对齐和签名两步才能在真机上装。这一章讲整个正向构建流程和签名参数细节。4.1 回编译apktool b 的常用姿势apktool b /path/to/apk_out -o /path/to/unsigned.apk参数说明b是 build回编译模式-o指定输出 apk 路径如果遇到资源编译报错可以加--use-aapt2后用 aapt2 做资源编译但 aapt2 报错信息更抽象我一般默认不开只有默认 aapt 失败时才追加这个参数重跑一遍。回编译出来的unsigned.apk还是未签名的这个状态直接往手机装必然报「未安装应用」。接下来做对齐和签名。4.2 资源对齐与签名工具链签名前先对齐。zipalign的作用是让 apk 内部资源以 4 字节对齐方式排布Android 系统读取资源时可以少做一次 copy省内存也降低启动时 IO 延迟。zipalign -f 4 /path/to/unsigned.apk /path/to/aligned.apk参数逻辑-f是 force覆盖同名输出文件4是字节对齐值固定填 4不要改在 Android 高版本中 zipalign 不再需要单独执行apksigner 签名时会自动处理对齐但我会习惯先对齐再签名特别是要保留 v1 签名时顺序错了会导致 v1 校验失败。签名工具用apksigner不要用老旧的jarsigner。jarsigner 只签 v1 方案Android 7.0 以上默认要求 v2目标版本高于 30 的包在 Android 14 上只带 v1 会直接被拒绝安装。如果手上没有现成密钥先生成一个测试用的keytool -genkey -v -keystore /path/to/my.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 3650密钥参数-validity 3650是有效期 10 年-keysize 2048是 RSA 位数。测试包 2048 够用上生产你需要 4096 或使用 ECDSA。正式签名apksigner sign \ --ks /path/to/my.keystore \ --ks-pass pass:你的密钥库密码 \ --key-pass pass:你的别名密码 \ --v1-signing-enabled true \ --v2-signing-enabled true \ --out /path/to/signed.apk /path/to/aligned.apkv1/v2/v3 签名方案怎么选我按 targetSdk 版本给一个参考表targetSdk必须签名方案说明≤ 27v1 即可老系统兼容性最好国产 ROM 普遍能装28 ~ 30v2 必需v1 建议保留姜饼Android 2.3以下机型忽略 v2只认 v1≥ 31v2/v3Android 11 强制 v2部分三星/鸿蒙机型要 v3 才能保证覆盖安装通过--v1-signing-enabled true是很多人漏掉的apksigner 默认策略会根据最低支持版本自动关闭 v1。你如果从网上下了一个要求 android:minSdkVersion24 的包apksigner 会认为所有设备都支持 v2于是把 v1 关了。结果某些国产 ROM 的系统包管理器仍走 v1 校验安装直接失败。所以我的习惯是无论 minSdk 多高显式打开 v1、v2 双方案兼容性最稳。签名后验证apksigner verify --verbose /path/to/signed.apk输出中会列出Verified using v1 scheme: true、Verified using v2 scheme: true等条目。任一项为 false 就要排查对应环节v1 false 往上查对齐顺序v2 false 往下查签名参数。4.3 一条命令跑完全流程把上面的步骤串成脚本省得每次手动敲#!/bin/bash APK_OUT/path/to/apk_out KEYSTORE/path/to/my.keystore KS_PASS你的密钥库密码 KEY_PASS你的别名密码 apktool b $APK_OUT -o /tmp/unsigned.apk \ zipalign -f 4 /tmp/unsigned.apk /tmp/aligned.apk \ apksigner sign \ --ks $KEYSTORE \ --ks-pass pass:$KS_PASS \ --key-pass pass:$KEY_PASS \ --v1-signing-enabled true \ --v2-signing-enabled true \ --out /tmp/signed.apk /tmp/aligned.apk \ apksigner verify --verbose /tmp/signed.apk这里有个小坑apktool b的输出路径和zipalign输入路径不能是同一个文件。apktool 回编译出的 apk 是未对齐状态zipalign -f覆盖它时会拿到还不完整的文件导致后续签名输出的包体积异常。我先输出到/tmp/unsigned.apk对齐到/tmp/aligned.apk串行执行各环节互不污染。5. 避坑排查改包路上五个高频翻车现场改包 90% 的时间不在「改」而在「改完装不上 / 装上闪退」。下面五条是我亲测过的坑按频率排序每条都是「现象 → 原因 → 解决」的完整链路。5.1 安装了却秒退NoClassDefFoundError现象签名包能装上一点开直接闪退logcat 报NoClassDefFoundError指向某个自定义类。原因smali 里类的引用写了简化名或者全限定类名路径和实际目录不一致。apktool 回编译时不检查跨类引用只会按Lcom/example/Test;-method()这样的完整路径去解析路径不对运行期才炸。解决用 jadx 反编译源码找到该类所在包名回 smali 目录里所有.smali文件中把L简化名;全局替换成L完整包名/类名;。替换后做一次完整性 grepgrep -rn Lcom/example/(没有包名的类) /path/to/apk_out/smali | head5.2 回编译报错aapt2 卡在 values-v21 目录现象apktool b失败报错信息类似resource style/Widget.AppCompat.* not found指向res/values-v21。原因解包时 apktool 默认保留版本限定资源目录但你只改了values/strings.xml没注意values-v21里存在同名字符串或 style 引用aapt2 在高版本 targetSdk 下对 style 父引用检查比 aapt 更严格抄错一处就整个编译中断。解决先看values-v21是否必须存在。如果只是改应用名不涉及夜间模式或 API 21 特有的样式直接删掉values-v21、values-night等非默认目录也能过编译。但注意删除前先确认没有android:theme显式引用了里面定义的 style有的话需要在values的默认目录里补一份同名的最小定义——这是最常见的翻车点。5.3 安装提示「应用未安装」INSTALL_PARSE_FAILED_NO_CERTIFICATES现象把签名 apk 传到手机安装弹窗提示「应用未安装」adb 安装报INSTALL_PARSE_FAILED_NO_CERTIFICATES。原因签名包里根本没有有效的证书条目。两种可能——一是漏了签名直接把unsigned.apk拿去装二是apksigner sign时把--v1-signing-enabled显式关掉了而设备系统包管理器只尝试读 v1 签名块。解决先跑apksigner verify --verbose看方案状态确认没有证书后再重新签。我在前面第 4 章已经把参数写成--v1-signing-enabled true --v2-signing-enabled true这个场景直接复跑签名命令即可。5.4 改包名后分享/支付全失效现象把包名从com.old.app全局替换成com.new.app后应用能启动但微信分享、支付回调全部失败甚至出去页面再回来闪退。原因Android 的FileProvider和各 SDK 的授权回调机制是按 authority 或包名校验的。你改了 package 标签但res/xml/file_paths.xml里的 authority 硬编码还是旧包名分享图片时生成content://URI 和系统注册的 provider 对不上系统直接拒绝访问。解决修改包名时同步做三件事grep -rn com.old.app /path/to/jadx_out --include*.java | grep -v R.java | head -20找到的硬编码处除了 AndroidManifest 的package属性要同时替换provider标签的android:authorities值代码里传context.getPackageName()的地方不用管但 SDK 内部缓存包名的字段要全局替换如果原包调用了ContentResolver的FILE_PROVIDER快捷方式检查 FileProvider 的配置路径。替换完成后重启换回一个新 apk 再签名避免测试机上旧的 authority 缓存干扰判断。5.5 加固包 apktool 直接报错/解出来是空壳现象apktool d跑一半报错或解压成功后smali目录几乎没有文件只有几个壳加载器类。原因apk 是加固过的腾讯乐固、梆梆、爱加密等。apktool 只能拆常规 dex加固包的 dex 被加密壳封装真正的业务 dex 在运行时由壳动态解密静态解析自然看不到内容。解决先判断加固类型参考第 2.3 节的 grep 命令。遇到加固包且必须改逻辑思路只有两条脱壳或换包。脱壳本身需要对应壳版本的工具和更多逆向经验我一般会在 jadx 反编译结果里确认壳的版本后直接给需求方两个结论——「放弃改这个包找官方版」或「提供无 shell 的原包」不为难自己。对资源级修改图标、文案、assets 配置加固包通常连 res/ 都加密了apktool 也不可靠这类场景没有捷径老老实实找未加固的包再动手。6. 进阶验证用日志和签名校验确认改动真的生效改完、签完、能装上并不代表改对了。验证才是后悔药跑完这三步再交付能省掉后续大量「用户反馈还是旧版」的回锅时间。第一步是静态复核签名和包体结构。签完名后用 aapt 看包的基本信息确认包名、版本、启动 Activity 都是你预期值aapt dump badging /tmp/signed.apk | head -20重点看三行package:后面冒号前的包名、versionCode/versionName、launchable-activity。这三类信息容易混在多个 apk 里搞错尤其是在同一项目反复改包名时。第二步是设备端安装与崩溃日志验证。连接 Android 手机或模拟器执行adb install -r /tmp/signed.apk adb logcat -c adb shell monkey -p com.example.app --throttle 400 --count 300参数说明-r是覆盖安装保留数据-c清空历史日志避免被旧 crash 干扰monkey 是随机事件注入工具--throttle 400表示每次操作间隔 400ms--count 300执行 300 次随机点击。300 次跑完没有崩溃基本可以断定不是一启动就炸的硬伤。第三步是验证「改动内容真的生效」。这一步最直接换掉的应用名去系统设置 → 应用 → 对应包名检查显示名换掉的图标看桌面快捷方式。但资源和逻辑同时改的时候我习惯用 jadx 反编译签名后的 apk把改过的字符串再 grep 一遍确认没有回编译阶段把 xml 里的内容覆盖掉。反向验证虽然土却是最不容易骗自己的。从那以后我每次改完 apk 都强制走一遍验证链aapt 看包体信息 → Android 设备上用 monkey 跑随机事件 → jadx 二次反编译核验改动点。三个步骤加起来不到五分钟但至少帮我拦下过 9 次「以为自己改了、实际改错文件」的尴尬。希望帮到你。本文还有配套的精品资源点击获取