简介面向安卓开发者和逆向工程学习者这套资源围绕安装包反编译需求整合出一站式工具链。其中APKTool负责将资源文件解码为可读的XML格式并支持修改后重新打包自动签名工具用于快速为修改后的安装包生成有效数字签名Dex2Jar能把安卓虚拟机字节码转换成Java类库再配合界面化的JD-GUI直观浏览还原出的Java源码。整套流程覆盖解包、修改资源、重签名、字节码转换到源码查看的主要环节既适合学习他人实现技巧也适用于安全审计、恶意行为分析和自查应用漏洞。资源共58个文件以批处理脚本、Shell脚本、Jar工具、文本说明为主并包含少量可执行程序和密钥材料Windows与Linux环境均可直接使用整体体积仅10.58MB便携易用。目前已有13971人学习下载工具按功能分模块存放便于按需调用无论是刚接触逆向的新手还是需要快速处理安装包的开发者都能从中找到对应工具。1. 拿到一个 APK别指望一键还原做移动端开发或安全测试的人迟早会遇到这样的场景手里有一个 APK想看某个功能的实现思路或者要分析某个 SDK 的调用逻辑又或者要改图标、改包名、汉化某个海外工具。这时候第一反应就是搜 APK 反编译工具下载所谓的一键反编译神器。我最初也是这么干的下载了个一键工具点完按钮等着出 Java 源码结果工具跑完只吐出一堆 smali 文件和没头没尾的资源文件。APK 反编译工具能解包但不代表能直接给你一份可以重新编译的工程级源码。apktool、dex2jar、jd-gui 这三件套加 auto-sign 签名脚本是目前从业者最常用的一套组合能解决从资源提取、DEX 字节码转换、Java 代码阅读到重打包签名的全过程。这篇笔记会把这套链路的每一步走一遍尤其是重打包签名这一步网上教程大多一笔带过实际上坑最深。2. 工具链选型四个小工具各管一段别混着用2.1 apktool负责资源和 smali不负责 Java 代码apktool 在整个链条里的角色是拆解和重组它把 APK 当作一个 zip 包加一层解析逻辑来处理。执行apktool d之后得到的是资源文件、AndroidManifest.xml 的明文形式、以及 classes.dex 反汇编出来的 smali 字节码。很多人在这里有个误解觉得看到 smali 就完事了这玩意儿是寄存器级的中间表示人眼直接读非常痛苦。它更像是给后续 dex2jar 和 jd-gui 打前站。实际使用中apktool 的价值在于两点一是资源可控res 目录下的 XML 会被还原成可读的格式修改后再用apktool b打包回去二是适配性新版 apktool 对 Androguard 和 AAPT2 的兼容做得比较稳老版本根本解不开某些厂商加固后的资源。注意apktool 的解包不处理签名也不处理代码还原它的边界很清楚。2.2 dex2jar把 DEX 字节码转成 JAR但别期待还原变量名dex2jar 做的事情是把 classes.dex 里的 Dalvik 字节码转换成 JVM 的 class 字节码输出成一个 JAR 包。关于这个工具有个常见的玄学转出来的 JAR 在 jd-gui 里可能看不到源码只能看到方法签名和异常表这时候会有人怀疑工具坏了。实际上不是工具坏了是 dex2jar 内部依赖的混淆映射信息为空方法体反编译失败时会静默跳过这正是后面要提到的坑之一。工具链选型的时候要注意版本匹配dex2jar 2.x 版本在 2020 年之后就基本不再更新对于新的 DEX 格式尤其是加了某些厂商自定义 opcode 的包转换出来不完整是非常正常的。这种时候别硬用一个工具走到底可以跳到第 6 章看更务实的替代方案。但作为标准链路dex2jar 目前仍然是转 JAR 最快的方案。2.3 jd-gui阅读 JAR 源码的可视化终端jd-gui 是一个图形化工具它读取 dex2jar 产出的 JAR 包在内存里反编译 class 文件并展示成接近原始的 Java 代码。它的强项是快速浏览和搜索弱点是反编译的还原度在复杂控制流上会丢逻辑比如 switch 语句嵌套深了可能变成一堆 if-else循环边界也会偶尔错位。选型建议jd-gui 适合定位代码入口和关键调用的第一轮阅读不要拿它当作最终代码审查工具。它显示的代码是供人脑理解逻辑的不是可以拿回去编译的。如果 jd-gui 打开 JAR 报 Internal Error通常是 JAR 内 class 版本过高或者带上了 Java 11 的字节码特性3.x 版本的 jd-gui 对老项目的兼容明显比 1.x 强优先用最新版。2.4 auto-sign重打包的最后一道工序最容易翻车auto-sign 是一个签名脚本集合本质是把重打包后的 APK 用测试密钥签名让 Android 系统能正常安装。这个脚本对应的是一套固定的测试 keystore说白了就是一把大家共用的钥匙。它解决的是开发环境里快速安装验证的问题生产环境谁把 auto-sign 的密钥拿去上架那等于把自己的应用放进一个谁都能打开的箱子。工具链选型这里有个重要边界apktool 自带的重打包命令不签名dex2jar 不反编译资源jd-gui 不负责回写auto-sign 只管签名不管对齐。把边界理清了后面每一步的输入输出才顺。3. 把 APK 拆成素材和代码四项命令的完整链路3.1 环境准备和版本核对动手之前先把 Java 环境确认好整套工具链都跑在 JVM 上。JDK 8 到 JDK 11 之间是安全区我一般在 JDK 11 上跑 apktool 和 dex2jarJDK 8 跑 auto-sign原因是 auto-sign 脚本里用的 keytool 和 jarsigner 在更高版本 JDK 里默认算法变了默认 SHA-256 签名在 Android 4.2 以下的兼容性有问题虽然现在这层兼容已经不太有人关注但不希望在这个位置消耗时间。# 验证 Java 版本建议 JDK 8 或 11 java -version # 确认 apktool 版本 java -jar apktool.jar --versionapktool 的版本会影响解包行为2.6.0 以上的版本对 Android 10 及以后的应用资源 ID 处理更完整。版本写进这一步的原因是很多网上的教程还在用 2.4.0拿到新应用时会碰到资源 ID 溢出导致的重打包失败这不是操作问题是工具版本问题。3.2 解包命令apktool d 的参数与输出# 基础解包-o 指定输出目录 java -jar apktool.jar d target.apk -o app_source # 带 only-main-cls 模式只反编译主 dex适合快速定位 java -jar apktool.jar d target.apk -o app_source --only-main-cls # 禁止反编译资源只要 smali 代码速度更快 java -jar apktool.jar d target.apk -o app_source -r常用参数里--only-main-cls在只关心入口类时非常好用能控制解包后的体积节省后续 dex2jar 的转换时间。-r参数的意思是 keep resources as-is资源文件保持原始二进制不解码 XML这样解包速度会快不少代价是 res 目录下的 XML 全是二进制乱码如果目标是改造 UI 就别加这个参数。解包完成后检查输出目录重点确认AndroidManifest.xml是可读文本smali 目录下有真实的非空文件这一步没问题再往下走。3.3 dex 转 jardex2jar 的输出与常见失败点# 转换主 dex 文件 ./d2j-dex2jar.sh target.apk -o output_classes.jar # 对 apktool 解包后的 classes dex 目录批量转换 ./d2j-dex2jar.sh app_source/classes*.dex -o output_classes.jar这个命令有个细节值得注意第一个写法直接传 APK 文件dex2jar 内部会自己解压并找出所有 dex 文件第二个写法传的是 dex 文件通配符适合已经用 apktool 解过包的情况。参数-o指定输出文件路径不指定时会把名字写在当前目录下。多数情况下的失败原因只有两类一类是 dex 文件本身被加固过转换出来只有几个空壳 class另一类是内存不足大项目后直接 OutOfMemoryError。出现 OOM 时加一个 JVM 参数再执行# 扩大堆内存到 2GB ./d2j-dex2jar.sh -Xmx2g target.apk -o output_classes.jardex2jar 脚本本身是 shell 封装可以把 JVM 参数直接写在脚本入口的命令后面但注意脚本内部是靠d2j_invoke转调 java 的直接把-Xmx2g写在脚本后面不一定有效稳妥的做法是设置环境变量JAVA_OPTS再跑脚本。3.4 jd-gui 打开 jar 与第一个定位动作jd-gui 运行起来后把 output_classes.jar 拖进去左侧会按包名展开目录树。不要一进来就从上往下读正确的第一个动作是拿应用名拼音或包名关键词搜一下。按下快捷键或点击 File 下的 Search输入包名主后缀定位到 Application 子类或主 Activity。这一步能在秒级确认反编译是否成功如果点开类文件能看到完整的方法体代码链路就通了一半。常见问题看到的代码是throw new RuntimeException(Method not implemented)这种占位内容这代表 dex2jar 遇到混淆代码后无法还原方法体属于正常现象。处理的思路是跳过这里去全局搜索关键业务字符串比如 API 地址、AppKey、特征常量再从引用这些常量的方法读起。4. 重打包与签名实操apktool b 和 auto-sign 的配合细节4.1 修改资源后重建 APK 包第 3 章解包出来的目录就是重打包的输入。改资源文件时要保持目录结构不变尤其注意 res/values 下的 XML 如果被改动了格式比如把string节点的引号弄丢打包会直接失败。# 重打包为未签名 APK java -jar apktool.jar b app_source -o rebuilt.apk重打包的输入目录是解包时用-o指定的那个目录。-o指定输出文件路径如果不指定apktool 会在输入目录的 dist 子目录里生成 APK。这一步产出的 APK 是未签名的安装到机器上会提示解析包错误需要接签名流程。这里有个参数很重要b命令默认把 resources.arsc 重新构建如果解包时没用-r一般都能正常完成若是解包时加了-r保留了二进制资源重打包时不加任何参数也能过但改过的资源文件若以明文形式放进二进制目录打包后必崩。4.2 auto-sign 签名的执行与 JDK 版本注意auto-sign 脚本的核心是把 rebuilt.apk 用内置的测试密钥完成签名。它内部调用 keytool 和 jarsigner参数固定指向一套自带的 keystore。JDK 8 的 keytool 默认用 SHA1withRSAAndroid 5.0 及以上的系统都认这套。JDK 9 之后 jarsigner 默认签名算法切到了 SHA-256也兼容但脚本中如果用了-sigalg参数指定了旧算法高版本 JDK 会直接抛错。# 常规执行签名Windows 是 .batLinux/macOS 是 .sh ./sign.sh rebuilt.apk # 签名完成后生成 rebuilt.apk.signed.apk直接安装 adb install rebuilt.apk.signed.apk注意sign.sh执行时会先对 APK 做一次 zipalign这个工具在 auto-sign 包里自带了一个但它拿到 APK 之后会先删除原始 META-INF 目录下的签名文件再写入新的签名文件。如果你的 APK 在之前步骤中已经用 debug keystore 签过一次auto-sign 脚本也能覆盖但覆盖后签名指纹会变成 auto-sign 的固定指纹。每次取回签名后的 APK 都要确认输出文件后缀是.signed.apk脚本跑完没有任何提示不代表成功要用下面命令验证# 查看签名是否生效 apksigner verify --verbose rebuilt.apk.signed.apkapksigner 来自 Android SDK 的 build-tools优先用这个工具验证而不是 jarsignerapksigner 会显示签名方案 v1、v2 的启用情况。4.3 v1 与 v2 签名机制新版系统的关键门槛auto-sign 脚本产出的签名默认只有 v1 方案Android 7.0 及以上系统默认接收 v2 方案安装老式 v1 签名的 APK 时会多一层兼容提示大多数设备依然能装但存在一个硬性翻车点如果把 auto-sign 签出来的包拿到 Android 11 及以上并开启“增强型包验证”的机器上装会被射频拦截提示“应用未安装”。处理方式是在 auto-sign 签名后用 apksigner 追加 v2 签名# 追加 v2 签名保持 v1 不变 apksigner sign --ks auto-sign的密钥路径 --ks-pass pass:密码 \ --v1-signing-enabled true --v2-signing-enabled true \ rebuilt.apk.signed.apk这样一个包同时带 v1 和 v2新旧设备通吃。我在本地一直保留着一份 build-tools 里的 apksigner因为 auto-sign 里没有这个程序完整工具链补齐后重打包的路才算走通。4.4 重打包后安装崩溃的第一排查顺序重打包后频繁出现两类问题这里直接给排查顺序一是安装包解析错误优先确认签名是否执行成功再确认 zipalign 是否对齐如果 apktool b 时使用了--use-aapt2参数对齐工作 apktool 自己做再跑 auto-sign 脚本里的 zipalign 反而可能二次对齐引发偏移二是装上了但一打开就闪退这类问题大概率出在资源 ID 或签名校验逻辑上资源 ID 的排查要去比对 res/values/public.xml 里的 ID 映射表签名校验逻辑则要找 smali 里类似checkSignature、getPackageInfo、signature字符串的调用点找到后把验证分支改成无条件跳过或返回真值再重打包。5. 反编译避坑手册来自拆包一线的五条记录5.1 jd-gui 打开 jar 提示 Internal Error现象拖入 jar 文件后大部分类显示正常少数类双击就弹 Internal Error看不到任何代码。原因dex2jar 转换出来的 class 文件里含有新版 Java 字节码指令jd-gui 的 1.x 版本解析不了。常见触发点是项目里使用了 Lambda 表达式或者 Switch 字符串拼接生成的方法老版反编译器无法还原这些语法树的节点。解决先确认 dex2jar 和 jd-gui 都用各自的 2.x / 3.x 最新版本。如果问题依旧方法体里逻辑可能已经被混淆到了极深层次不再适合用 jd-gui直接跳过。我一般会改用命令行方式单独查看或者退而求其次看 smali 逻辑。5.2 dex2jar 转换完只有一个体积很小的 jar现象目标 APK 有 30 MB转完 jar 只有几十 KB查里面只有几个类其他全丢了。原因APK 使用了加固壳真实的 dex 在运行时才被解密释放。静态拆解时 classes.dex 只是一个壳入口真正的代码在 assets 目录下的加密文件或 so 库里。dex2jar 只会老老实实转 classes.dex转出来的自然是体积很大的壳代码。解决先识别壳种类常见的加固厂商在包名和 so 名字上都有明显特征。脱壳通常需要借助运行设备来 dump 内存中的 dex不在静态工具链的讨论范围内。如果目标是阅读业务代码并且找得到对应版本的应用市场渠道包优先找未加固的渠道版本解包体验会顺畅得多。5.3 apktool 解包后 res 目录下 XML 大量乱码现象解包成功后res/layout下的 XML 文件打开是类似ÿþ开头的二进制不是正常的标签文本。原因apktool 在解析时把资源当作原始二进制处理了。一种是解包命令带了-r参数另一种是资源在打包时用了非标准压缩方式apktool 的解码器认为它不可读。解决去掉-r参数重新解包。若仍乱码检查 APK 是否使用了资源混淆插件资源混淆会把文件路径和 XML 头打乱加--use-aapt2参数重试。5.4 auto-sign 执行后提示 keytool 错误现象运行 sign.sh输出一串和密钥库格式有关的异常签名失败。原因高版本 JDK 的 keytool 默认生成的密钥库格式是 PKCS12auto-sign 内置脚本读取的是旧版 JKS 格式的密钥库路径格式不匹配时报错。解决优先使用 JDK 8 运行 auto-sign 脚本或者在脚本的-storetype参数后面手动指定成 PKCS12确保和本机 keytool 默认格式一致。5.5 重打包后运行闪退且 Logcat 看不到 Java 异常现象加了少量资源修改重打包打开直接退出Logcat 没有任何 AndroidRuntime 异常堆栈。原因闪退发生在 Native 层。常见于壳校验、so 文件加载失败、或资源 ID 变化导致系统 UI 在 frame 渲染阶段崩溃Java 层根本没有机会打印异常。解决先检查 so 文件在解包和重打包过程中是否被损坏用 unzip 对比原始 APK 和 rebuilt APK 中lib/目录文件名是否一致再确认是否使用了--use-aapt2如果原 APK 是 AAPT2 打包重打包时也要用 AAPT2否则资源 ID 映射表对不上。最后才去看 smali 签名校验逻辑。6. 一条效率翻倍的进阶路径从 Manifest 入口向调用链下游推整套工具链跑通之后大多数人的反编译体验还停留在到处搜字符串、碰运气看代码的阶段。这里分享一个在实际项目中反复验证过的读取路径能让定位效率高出好几倍。打开 apktool 解包目录下的AndroidManifest.xml搜索android.intent.action.MAIN。这个 action 和android.intent.category.LAUNCHER组合在一起的 activity是应用的启动入口也是逻辑链的天然起点。在 jd-gui 里找到对应这个 activity 的类名先读onCreate方法之后顺藤摸瓜找findViewById和setOnClickListener的调用点再到监听器内部看具体的业务分支。一个有价值的技巧是大部分商业应用会在 Application 子类里做全局初始化包括三方 SDK 的注册、路由表的加载、进程 fork 判断。jd-gui 左侧的类名中包名路径通常带有app或者base字样的就是入口模块。从这里开始比从任何字符串搜索切入都要直接。另外一个进阶验证方法修改入口 Activity 的可见性。在 apktool 解包后的 Manifest 里给某个不导出的 Activity 加上android:exportedtrue在真机上用 adb 命令直接拉起它检查这个页面对数据输入的防御机制。重打包和签名流程走的是第 4 章的完整链路唯一的区别是这次修改点在 Manifest 而不在 res 或 smali 里。这个操作能快速验证一个页面是否存在未授权访问风险也是移动端测试常见用例。有一次排查某个应用升级逻辑时我把升级检测相关类里所有字符串都搜了一遍没找到 API 地址后来顺着 Application 子类的执行顺序读发现调用栈藏在 startActivity 的 target 参数里而这个 target 值是从 so 层读取后通过 JNI 回调传上来的。从那以后我每次拿到样本都会强制走一遍「Manifest 找入口 → Application 读初始化 → Activity 读交互 → 触发点回溯」的流程复杂度高的样本也不再抱怨。希望这些习惯和踩坑记录能帮你在工具链上少花冤枉时间。本文还有配套的精品资源点击获取