游客发表
APK 本质上就是编译个 ZIP 包。你把任意一个 Android 应用的工具后缀改成 .zip,解压出来就能看到 classes.dex、编译resources.arsc、工具AndroidManifest.xml 这些文件。编译反编译要做的工具,就是编译把这些二进制格式还原成人类能读的东西。dex 变 Java 源码,工具arsc 变可读的编译资源表,Manifest 变明文 XML。工具
最常用的编译组合是 apktool + jadx + dex2jar。apktool 负责拆资源和 smali 汇编,工具jadx 直接把 dex 转成 Java 代码,编译dex2jar 则是工具把 dex 转成 .jar 再用 JD-GUI 看。三个工具各管一段,编译实际干活时经常混着用。
apktool 的命令很直白:
apktool d target.apk -o output_dir 拆完之后你会得到 smali 目录、res 目录、AndroidManifest.xml。smali 是 Dalvik 字节码的汇编表示,语法有点像汇编语言,读起来不算友好,但改起来精确。比如你想把一个付费验证的返回值从 false 改成 true,在 smali 里找到对应的方法,把 const/4 v0, 0x0 改成 const/4 v0, 0x1 就行。改完用 apktool b 重新打包,签名,装回手机。
apktool 的短板是 smali 可读性差。逻辑一复杂,满屏的 move-result、invoke-virtual 看得人头疼。所以我一般用它处理资源替换和简单逻辑修改,复杂逻辑分析交给 jadx。
jadx 是我日常用得最多的工具。它直接把 APK 拖进去就能看 Java 代码,不需要中间步骤。jadx-gui 的搜索功能也够用,支持按类名、方法名、字符串搜索。
不过 jadx 的反编译不是百分之百准确。遇到混淆过的代码,变量名全是 a、b、c,控制流也可能被打乱。有些方法它反编译失败,会退化成 smali 显示。这不怪 jadx,是混淆工具(比如 ProGuard、R8)故意制造的障碍。
实际经验:如果一个 APK 没做混淆,jadx 出来的代码几乎和原始源码差不多。一旦上了混淆加字符串加密,阅读成本会翻好几倍。我遇到过用 Allatori 混淆的样本,jadx 出来的代码里嵌套了十几层无意义的 if-else,纯粹是为了恶心逆向的人。
dex2jar 的流程是先把 APK 解压取出 classes.dex,然后:
d2j-dex2jar.sh classes.dex 生成 .jar 文件后丢进 JD-GUI 看。这套组合比 jadx 老,但在某些 jadx 反编译失败的场景下,JD-GUI 反而能出结果。两个工具的反编译引擎不同,互为补充。
缺点是 dex2jar 对多 dex 支持麻烦,Android 应用超过 65K 方法就得拆成 classes2.dex、classes3.dex,得逐个转换。jadx 没这个问题,直接全读。
静态反编译看不到运行时才解密的东西。有些应用把关键字符串加密存在 so 库里,运行时才解密到内存。这时候光靠 jadx 没用,得上 Frida。
Frida 可以 hook 任意方法,打印参数和返回值。比如你怀疑某个方法在做签名校验,用 Frida hook 它,看输入输出是什么,比硬读 smali 快得多。Xposed 类似,但需要 root 和重启设备,Frida 更灵活。
我一般的流程是:jadx 先过一遍找关键类和方法,定位到可疑点后用 Frida hook 验证,确认逻辑后再回到 smali 里做修改。静态和动态结合,效率比纯读代码高很多。
apktool 重新打包后必须重新签名才能安装。用 keytool 生成一个自签名证书,再用 apksigner 签:
apksigner sign --ks my.keystore --out signed.apk output.apk 如果目标应用有签名校验(很多金融类、游戏类都有),重签名后启动会直接闪退。校验逻辑可能在 Java 层,也可能在 native 层。Java 层的校验用 jadx 找到后改掉或 hook 掉就行。native 层的麻烦一些,得用 IDA 分析 so 库,或者用 Frida 直接 hook JNI 函数。
签名校验是反编译过程中最常翻车的地方。我见过一个应用在 onCreate 里做了三重校验:Java 层比对签名哈希、JNI 层检查 APK 路径、还有个定时器每隔几秒复查一次。这种就得三处全改,漏一个都跑不起来。
工具就这些,关键还是耐心。一个中等复杂度的应用,从拆包到定位关键逻辑,顺利的话半天,遇到混淆加壳的可能要磨好几天。壳的问题另说,那是另一个话题了。
友情链接