简介:Androidfby反编译工具面向Android开发者与逆向工程学习者,用于解析APK内部结构,查看Java源代码、资源文件与XML配置,便于分析应用工作原理或在修改、优化、调试APK时提供辅助。压缩包共488个文件,约8.84MB,以pgt、ogg等资源文件为主,另含jar库文件、xml配置、dex字节码、exe主程序及示例apk,并附readme说明、日志与lib依赖目录,目录结构清晰,便于按模块检索。目前已有621人学习下载。借助其中的32位与64位可执行程序、示例APK和说明文档,读者可快速上手反编译流程,查看反编译结果并导出到本地深入分析,同时结合日志与库文件排查运行问题,理解APK内部机制,提升开发与调试效率。使用时请注意版权与隐私安全,遵循合法合规原则。
1. Androidfby反编译工具:从APK到可读源码,一条能跑通的逆向链路
一个只有APK、没有源码的Android项目摆在面前,产品说“就改个按钮文案”,你解压一看全是.dex和二进制XML,这时候Androidfby反编译工具就是那根救命稻草。它不是一个单一程序,而是一套围绕APK逆向的组合拳:把classes.dex还原成Java、把resources.arsc还原成可读XML、把图片和布局从压缩包里剥出来。做安全审计、做竞品分析、做老项目维护,甚至只是找回自己三年前弄丢的源码,都绕不开这条链路。这篇笔记不讲玄学,只讲我实际跑通过的步骤、参数和翻车点,新手能照着敲,熟手能对着边界条件做取舍。
2. 反编译链路拆解:dex、arsc、资源各用什么工具
Android的APK本质上是个zip,但里面的东西不是给人看的。classes.dex是Dalvik字节码,resources.arsc是编译后的资源索引表,AndroidManifest.xml是二进制格式,res/下的XML也被压成了二进制。所谓“反编译”,就是把这些二进制一层层还原回人类可读的形态。不同层用不同工具,混用会出问题。
2.1 dex转Java:为什么jadx和dex2jar要分场景用
classes.dex转Java有两条主流路线。第一条是jadx,它直接把dex反编译成Java并内置GUI,适合快速阅读和搜索。第二条是dex2jar加JD-GUI,先把dex转成jar,再用Java反编译器看。两者不是替代关系,是互补关系。
jadx的优点是快、集成度高、支持批量导出;缺点是遇到混淆严重的代码,反编译结果可能直接报错或生成一堆goto。dex2jar的优点是中间产物是标准jar,可以丢给任意Java反编译器,遇到jadx翻车的类可以换工具再试。
我一般先用jadx整体过一遍,定位到关键类之后,如果某个方法反编译失败,再用dex2jar单独处理那个dex,换JD-GUI或Procyon看。这个组合能覆盖九成以上的场景。
# 用jadx命令行反编译整个APK,输出到out目录 # -d 指定输出目录,--show-bad-code 让反编译失败的类也输出(方便定位) jadx -d ./out --show-bad-code target.apk # 如果只想看某个包,可以用 --classes 过滤 jadx -d ./out --show-bad-code --classes com.example.target target.apk--show-bad-code这个参数很关键。默认情况下jadx遇到反编译失败的类会跳过,你根本不知道丢了什么。加上它之后,失败的类会以注释形式输出,至少能看到方法签名和部分逻辑,方便判断是放弃还是换工具。
# dex2jar路线:先把APK里的dex转成jar # 注意:新版dex2jar对APK直接支持,不用手动解压 d2j-dex2jar.sh target.apk -o target.jar # 然后用JD-GUI打开target.jar,或者用命令行反编译器 # 这里用procyon做示例,它对Java 8+语法支持较好 java -jar procyon-decompiler.jar target.jar -o ./srcdex2jar的坑在于它对多dex的处理。APK里有classes.dex、classes2.dex、classes3.dex时,d2j-dex2jar.sh默认只处理第一个。必须手动指定所有dex,或者用--multi-dex参数。我见过有人反编译完发现少了一半类,排查半天才发现是multidex没处理。
2.2 资源还原:apktool的定位和它不做什么
apktool负责的是资源层:解码resources.arsc、还原二进制XML、把res/目录下的文件还原成可编辑格式。它不负责dex转Java,那是jadx和dex2jar的事。很多人把apktool和jadx混为一谈,结果用apktool反编译完发现Java代码还是乱的。
# 用apktool解码APK资源 # d 表示decode,-f 强制覆盖已有目录,-o 指定输出目录 apktool d -f target.apk -o ./decoded # 解码后目录结构: # decoded/ # AndroidManifest.xml (已还原为可读XML) # res/ (布局、字符串、图片等) # smali/ (dex反汇编成的smali代码) # apktool.yml (元信息,重打包时用)apktool输出的是smali而不是Java。smali是Dalvik字节码的汇编表示,可读性介于字节码和Java之间。如果你只需要改资源、改Manifest、改smali里的常量,apktool就够了。如果你需要理解业务逻辑,还是得靠jadx。
-f参数在反复调试时很有用,但要注意它会直接删掉输出目录。如果你在decoded目录里手动改过东西,加-f会全部丢失。我一般第一次用-f,后续调试不加,让apktool自己处理增量。
2.3 工具链的版本匹配:为什么你的反编译结果和别人不一样
反编译结果对工具版本极其敏感。同一个APK,jadx 1.4和jadx 1.5的输出可能完全不同。apktool 2.7和2.9对同一个resources.arsc的解码结果也可能有差异。这不是bug,是Android版本演进和工具适配的正常现象。
我的习惯是固定一套版本组合,记录在项目笔记里。比如jadx 1.4.7 + apktool 2.7.0 + dex2jar 2.1,这套组合在我手里跑过几十个APK,行为可预期。换版本之前先在小样本上验证,确认输出差异可接受再切换。
另一个容易被忽略的点是Java运行环境。jadx和apktool都是Java程序,JDK版本会影响反编译结果。JDK 8和JDK 11跑同一个jadx,遇到某些字节码指令时行为可能不同。我一般用JDK 11,兼容性和稳定性比较平衡。
3. 从APK到可读工程:完整操作流程与参数说明
这一章把整条链路串起来,从拿到APK到得到一个能搜索、能改、能重打包的工程目录。每一步都给出命令和参数说明,照着做就能复现。
3.1 环境准备:JDK、jadx、apktool、dex2jar的安装与验证
先确认JDK可用。jadx和apktool都依赖Java运行时。
# 检查Java版本,建议JDK 11或以上 java -version # 如果输出类似 openjdk version "11.0.x" 就OK # 如果提示command not found,需要先装JDKjadx的安装最简单,从发布页下载zip解压即可。apktool需要两个文件:apktool.jar和包装脚本apktool(Windows下是apktool.bat)。dex2jar同样解压即用。
# 验证jadx可用 jadx --version # 验证apktool可用 apktool --version # 验证dex2jar可用 d2j-dex2jar.sh --help三个工具都验证通过后,建一个工作目录,把待处理的APK放进去。我习惯按APK名字建子目录,避免多个项目混在一起。
mkdir -p work/target && cd work/target # 把target.apk放到当前目录 ls -la target.apk3.2 第一步:apktool解码资源,拿到Manifest和res
先跑apktool,因为它的输出目录结构最清晰,能快速判断APK的基本情况。
# 解码APK,输出到decoded目录 apktool d -f target.apk -o decoded # 查看解码结果 ls decoded/ # 应该看到 AndroidManifest.xml、res/、smali/、apktool.yml解码完成后,第一件事是看AndroidManifest.xml。重点看package属性、android:versionName、application标签下的android:name(入口Application类)、以及activity的android:name。这些信息决定了后续在jadx里搜什么。
<!-- decoded/AndroidManifest.xml 片段示例 --> <manifest package="com.example.target" ...> <application android:name=".App" ...> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest>如果android:name是.App,说明完整类名是com.example.target.App。在jadx里直接搜这个类名,就能定位到入口。
res/values/strings.xml是另一个重点。很多关键字符串、API地址、硬编码的key都在这里。apktool解码后,strings.xml已经是可读的XML,直接搜关键词就行。
3.3 第二步:jadx反编译dex,定位核心逻辑
apktool给了资源,jadx给Java代码。两个工具的输出目录分开,避免互相覆盖。
# 用jadx反编译,输出到jadx-out目录 jadx -d ./jadx-out --show-bad-code --deobf target.apk # --deobf 尝试反混淆,对简单混淆有效 # --show-bad-code 保留反编译失败的类--deobf参数值得说一下。它会对混淆后的类名、方法名做启发式还原,比如把a.b.c还原成com.example.target.util.NetworkUtil。效果取决于混淆强度,简单混淆能还原七八成,强混淆基本无效。但即使还原不准确,它也能让类名变得有规律,方便批量搜索。
反编译完成后,在jadx-out/sources/下就是Java源码。用IDE打开这个目录,或者直接用grep搜索。
# 搜索关键字符串,比如API地址 grep -r "https://" jadx-out/sources/ --include="*.java" | head -20 # 搜索特定类名 find jadx-out/sources -name "MainActivity.java" # 搜索特定方法调用 grep -r "getSharedPreferences" jadx-out/sources/ --include="*.java"jadx的GUI版本更适合交互式阅读。命令行版本适合批量处理和脚本化。我一般先用命令行跑一遍,确认反编译成功率,再用GUI打开做精细阅读。
3.4 第三步:dex2jar补漏,处理jadx翻车的类
jadx不是万能的。遇到某些字节码模式(比如复杂的try-catch嵌套、Lambda表达式、Kotlin协程),jadx可能生成错误代码或直接失败。这时候dex2jar加JD-GUI是备选方案。
# 先用dex2jar把APK转成jar d2j-dex2jar.sh target.apk -o target.jar --multi-dex # 如果APK有多个dex,--multi-dex会把它们合并到一个jar # 然后用JD-GUI打开target.jar,或者用procyon命令行反编译 java -jar procyon-decompiler.jar target.jar -o ./procyon-src--multi-dex参数在处理大型APK时必加。不加的话,只有classes.dex会被处理,classes2.dex及之后的全部丢失。这个坑我踩过不止一次,反编译完发现少了一半类,排查半天才想起来是multidex。
procyon的反编译质量在某些场景下比jadx好,尤其是对Java 8的Lambda和Stream API。但procyon对Android特有的字节码指令支持不如jadx。所以我的策略是:jadx为主,procyon为辅,两边对照着看。
3.5 重打包与签名:改完资源后怎么装回去
反编译的最终目的往往是修改。改完res/下的资源或smali/下的代码后,需要用apktool重新打包,然后签名才能安装。
# 重新打包,输出到new.apk apktool b decoded -o new.apk # 生成签名密钥(如果还没有) keytool -genkey -v -keystore my.keystore -alias mykey -keyalg RSA -keysize 2048 -validity 10000 # 用apksigner签名(Android SDK自带) apksigner sign --ks my.keystore --ks-key-alias mykey --out signed.apk new.apk # 验证签名 apksigner verify signed.apkapktool b的坑在于,如果decoded目录里有apktool无法识别的文件(比如手动加进去的二进制文件),打包会失败。解决办法是把这些文件放到decoded/assets/或decoded/res/raw/下,这两个目录apktool会原样打包。
签名后的APK才能安装到设备上。如果只是调试,可以用adb install -r signed.apk覆盖安装。注意覆盖安装要求签名一致,如果原APK和你新生成的签名不同,需要先卸载原应用。
4. 避坑与排查:反编译过程中最常见的5个翻车现场
反编译是个脏活,工具链长、版本多、APK本身还可能做了各种防护。这一章记录我实际踩过的坑,每条按现象、原因、解决来写。
4.1 反编译后Java代码全是a.b.c,混淆怎么破
现象:jadx输出的Java代码里,类名全是a、b、c,方法名全是a()、b(),完全无法阅读。
原因:APK经过了ProGuard或R8混淆,类名和方法名被替换成了无意义的短名称。这是正常现象,不是工具问题。
解决:分三步走。第一步,用jadx的--deobf参数尝试自动反混淆,能还原一部分。第二步,从AndroidManifest.xml里的入口类开始,顺着调用链手动追踪。混淆只改名字,不改逻辑,入口类的onCreate方法里调用的方法,即使名字是a(),也能通过上下文推断用途。第三步,如果APK有字符串加密,搜strings.xml里的明文关键词,反查调用位置。
# 用jadx的--deobf参数 jadx -d ./out --deobf --show-bad-code target.apk # 如果效果不好,试试用procyon配合自定义的映射文件 # 但映射文件通常拿不到,所以手动追踪是主要手段4.2 apktool解码报错“Could not decode arsc file”
现象:apktool d执行到一半报错,提示无法解码resources.arsc。
原因:APK使用了较新的资源格式,或者apktool版本过旧。Android 12之后引入了一些新的资源类型,老版本apktool不认识。
解决:升级apktool到最新版。如果最新版也不行,试试用--no-res参数跳过资源解码,只反编译dex和Manifest。
# 跳过资源解码 apktool d -f --no-res target.apk -o decoded-nores # 然后用jadx单独处理资源 jadx -d ./jadx-out --show-bad-code target.apk--no-res的代价是res/目录下的文件不会被还原,但AndroidManifest.xml和smali/仍然可用。对于只需要看代码的场景,这个取舍是值得的。
4.3 jadx反编译结果里方法体为空或只有throw
现象:jadx输出的Java文件里,某些方法只有一行throw new UnsupportedOperationException(),或者方法体是空的。
原因:jadx遇到无法处理的字节码模式,选择了保守输出。常见于Kotlin协程、动态代理、反射调用密集的代码。
解决:换dex2jar加procyon再试。如果还是不行,直接看smali。apktool输出的smali/目录里是完整的字节码汇编,虽然可读性差,但信息不丢失。
# 在smali目录里搜索目标方法 grep -r "methodName" decoded/smali/ --include="*.smali" # smali文件里能看到完整的指令序列 # 虽然需要熟悉Dalvik指令集,但至少不会丢逻辑4.4 重打包后安装失败“INSTALL_FAILED_INVALID_APK”
现象:apktool b打包成功,apksigner签名成功,但adb install报错。
原因:通常是资源文件损坏或Manifest格式错误。apktool在打包时对资源做了重新编码,如果res/下有apktool不认识的格式,打包出来的APK就是坏的。
解决:先用apktool b的--use-aapt2参数试试,aapt2对资源格式的兼容性更好。如果还不行,检查decoded/res/下有没有手动修改过的文件,特别是XML和图片。图片必须是标准PNG或JPEG,XML必须是合法格式。
# 用aapt2打包 apktool b decoded -o new.apk --use-aapt2 # 如果还失败,用aapt2单独验证资源 aapt2 compile --dir decoded/res -o compiled.zip4.5 反编译出来的代码能编译但运行崩溃
现象:反编译的Java代码看起来没问题,用Android Studio编译也能过,但装到设备上一跑就崩。
原因:反编译代码和原始代码有细微差异。比如泛型信息丢失、注解丢失、try-catch范围变化。这些差异在编译期不报错,但运行期会触发ClassCastException或NoSuchMethodError。
解决:不要试图直接编译反编译代码。反编译的目的是阅读和理解,不是重建可编译工程。如果需要修改逻辑,改smali比改Java更可靠,因为smali是字节码的直接映射,不会引入编译期优化带来的差异。
# 改smali后重新打包 # 修改decoded/smali/com/example/target/MainActivity.smali apktool b decoded -o new.apk apksigner sign --ks my.keystore --ks-key-alias mykey --out signed.apk new.apk adb install -r signed.apk5. 进阶技巧:用脚本批量处理与反编译结果验证
单个APK的反编译流程跑通之后,下一步是批量化。做安全审计时经常要处理几十上百个APK,手动一个个跑不现实。这一章给一个可复用的脚本框架,以及验证反编译结果完整性的方法。
5.1 批量反编译脚本:一次处理整个目录的APK
脚本的核心逻辑是遍历目录、对每个APK依次跑apktool和jadx、把输出按APK名字归档、记录成功和失败清单。
#!/bin/bash # batch_decompile.sh # 用法:./batch_decompile.sh /path/to/apk_dir /path/to/output_dir APK_DIR="$1" OUT_DIR="$2" LOG_FILE="$OUT_DIR/decompile.log" mkdir -p "$OUT_DIR" echo "=== Batch decompile started at $(date) ===" > "$LOG_FILE" for apk in "$APK_DIR"/*.apk; do name=$(basename "$apk" .apk) echo "Processing: $name" | tee -a "$LOG_FILE" # apktool解码资源 apktool d -f "$apk" -o "$OUT_DIR/$name/apktool" 2>> "$LOG_FILE" if [ $? -ne 0 ]; then echo " [FAIL] apktool failed for $name" | tee -a "$LOG_FILE" continue fi # jadx反编译dex jadx -d "$OUT_DIR/$name/jadx" --show-bad-code --deobf "$apk" 2>> "$LOG_FILE" if [ $? -ne 0 ]; then echo " [WARN] jadx had errors for $name" | tee -a "$LOG_FILE" fi # 统计输出文件数 java_count=$(find "$OUT_DIR/$name/jadx" -name "*.java" 2>/dev/null | wc -l) smali_count=$(find "$OUT_DIR/$name/apktool/smali" -name "*.smali" 2>/dev/null | wc -l) echo " [OK] $name: $java_count java files, $smali_count smali files" | tee -a "$LOG_FILE" done echo "=== Batch decompile finished at $(date) ===" >> "$LOG_FILE"脚本里用tee -a同时输出到终端和日志文件,方便实时观察和事后排查。continue在apktool失败时跳过当前APK,避免后续步骤在空目录上执行。
java_count和smali_count是两个关键指标。如果某个APK的java文件数为0,说明jadx完全失败,需要单独处理。如果smali文件数异常少,说明APK可能做了dex加密或壳保护。
5.2 反编译完整性验证:怎么判断结果可不可信
反编译结果的质量参差不齐,需要一个快速判断的方法。我的做法是看三个指标:类覆盖率、方法体完整率、资源还原率。
类覆盖率:jadx输出的Java类数量,对比apktool输出的smali类数量。正常情况下两者应该接近。如果Java类数量远少于smali类数量,说明jadx大量失败。
# 统计jadx输出的Java类数量 find jadx-out/sources -name "*.java" | wc -l # 统计apktool输出的smali类数量 find decoded/smali -name "*.smali" | wc -l # 如果Java类数量不到smali类数量的一半,说明反编译质量堪忧方法体完整率:随机抽几个Java文件,看方法体是否为空或只有throw。如果超过三成的方法体不完整,说明这个APK的反编译结果只能做参考,不能作为分析依据。
资源还原率:检查decoded/res/下的XML文件是否能正常打开。如果大量XML是二进制格式(用文本编辑器打开是乱码),说明apktool的资源解码失败。
# 检查res目录下有多少XML文件是二进制格式 # 二进制XML的文件头是 0x03 0x00 0x08 0x00 for f in $(find decoded/res -name "*.xml"); do if xxd -l 4 "$f" | grep -q "0300 0800"; then echo "Binary XML: $f" fi done5.3 从反编译结果里提取API接口和硬编码密钥
反编译的一个常见需求是找API接口和硬编码密钥。手动搜效率低,用脚本批量提取。
# 提取所有HTTP/HTTPS URL grep -rhoE "https?://[a-zA-Z0-9./?=_%:-]+" jadx-out/sources/ --include="*.java" | sort -u > urls.txt # 提取可能的API key(长度20以上的字母数字混合字符串) grep -rhoE "\"[a-zA-Z0-9]{20,}\"" jadx-out/sources/ --include="*.java" | sort -u > possible_keys.txt # 提取SharedPreferences的key名 grep -rhoE "getSharedPreferences\(\"[^\"]+\"" jadx-out/sources/ --include="*.java" | sort -u这些脚本提取的结果需要人工筛选。possible_keys.txt里大部分是误报,但只要有几条是真的,就值回票价。我一般把提取结果和strings.xml里的内容交叉比对,strings.xml里出现的字符串如果在Java代码里被引用,可信度更高。
5.4 一个我反复用的习惯:先跑apktool再跑jadx
最后说一个习惯问题。很多人拿到APK第一反应是直接丢给jadx,但我总是先跑apktool。原因有三个:第一,apktool输出的AndroidManifest.xml能快速告诉我入口类是什么,省去在jadx里盲目搜索的时间。第二,apktool的smali/目录是完整的字节码映射,当jadx结果可疑时,smali是最后的真相来源。第三,apktool的解码过程会暴露APK的资源结构,如果资源解码失败,说明APK可能做了加固,后续分析策略要调整。
这个顺序看起来多了一步,但实际节省的时间远超预期。反编译是个需要耐心的活,工具链的每一步都有它的价值,跳过任何一步都可能在后面付出代价。希望帮到你。
本文还有配套的精品资源,点击获取