5、静态分析工具实战:JADX与GDA反编译器使用技巧、代码混淆与反混淆初步

好,咱们今天聊点实在的。静态分析,说白了就是不动手跑代码,直接看源码。你可能会问:“App都加壳了,我看什么?” 嗯,别急。加壳只是第一道门,脱壳之后,你面对的就是一堆DEX文件。这时候,JADX和GDA就是你的左膀右臂。

我个人习惯,先拿JADX扫一遍整体结构,再用GDA做深度分析。为什么?JADX的树形目录清晰,适合看逻辑;GDA的交叉引用强,适合追数据流。今天我就把这两个工具的使用技巧,以及代码混淆的初步对抗方法,一次性讲透。

5.1 JADX:快速定位核心逻辑

JADX是我用得最多的反编译器。它能把DEX直接转成可读的Java代码,而且支持搜索、书签、反混淆。我在项目中遇到过好几次,客户说“这个App逻辑很简单”,结果一打开JADX,发现几千个类……嗯,这时候搜索功能就救命了。

5.1.1 常用操作技巧

  • 全局搜索(Ctrl+Shift+F):搜字符串、类名、方法名。比如搜“http://”或“api”,快速定位网络请求。
  • 跳转到声明(F4):看到某个方法调用,直接跳过去看实现。
  • 反混淆开关:在菜单栏勾选“反混淆”,JADX会自动重命名混淆后的类和方法。虽然不能100%还原,但至少能看出点规律。
  • 导出Gradle项目:File -> Save as Gradle project,直接导出可编译的工程。我经常用这个功能做二次开发或动态调试。
我的小技巧:遇到混淆严重的类,先看它的父类和接口。比如一个类继承自AsyncTask,那它八成是做网络请求的。别被乱七八糟的类名吓到,看继承链就行。

5.1.2 代码示例:定位关键方法

假设我们要找登录逻辑。在JADX里搜“login”或“password”,你会看到类似这样的代码:

public class a implements b {
    public String c(String str) {
        // 实际是加密密码
        return d.a(str);
    }
}

你看,类名和方法名都被混淆了。但通过搜索字符串“password”,我们能找到调用点。这时候右键 -> “查找用例”,就能看到谁调用了这个方法。这就是静态分析的威力——不用跑代码,就能画出调用链。

5.2 GDA:深度数据流分析

GDA是我最近两年才用顺手的工具。它比JADX强在哪?交叉引用和寄存器分析。JADX适合看逻辑,GDA适合看数据怎么流动的。我记得有一次分析一个恶意App,JADX里看到的代码全是乱序的,但GDA的CFG(控制流图)直接帮我理清了执行路径。

5.2.1 GDA核心功能

功能 说明 我的用法
交叉引用(Xref) 查看某个方法被谁调用,调用了谁 追加密函数的调用链
寄存器分析 显示每个寄存器的赋值和引用 分析混淆后的字符串拼接
控制流图(CFG) 图形化展示代码执行路径 理解混淆后的跳转逻辑
DEX编辑器 直接修改DEX字节码 临时patch掉反调试
注意:GDA的交叉引用是基于静态分析的,如果代码用了反射或动态加载,它可能找不到所有调用点。这时候需要结合动态调试。

5.2.2 实战:用GDA分析字符串混淆

很多App会把关键字符串拆成多个片段,运行时拼接。比如:

String s1 = "aGVsbG8=";  // base64编码的"hello"
String s2 = "d29ybGQ=";  // base64编码的"world"
String result = new String(Base64.decode(s1 + s2, 0));

在GDA里,你可以右键点击字符串变量,选择“查看交叉引用”,就能看到这个字符串在哪里被使用。然后顺着寄存器追踪,找到拼接和decode的逻辑。我曾经用这个方法,从一个金融App里挖出了硬编码的AES密钥。

5.3 代码混淆与反混淆初步

混淆,说白了就是让代码变得“不像人写的”。常见的混淆手段有:

  • 名称混淆:类名、方法名变成a、b、c
  • 控制流混淆:插入垃圾代码、打乱执行顺序
  • 字符串加密:关键字符串运行时解密
  • 反射调用:通过字符串动态调用方法

你想想看,如果代码里全是“a.b(c)”这种调用,你怎么分析?嗯,这时候就需要反混淆了。

5.3.1 手动反混淆技巧

我一般不依赖自动化工具,因为混淆规则千变万化。我的做法是:

  1. 先看入口点:比如Application的onCreate,或者Activity的onCreate。从这里开始,顺着调用链往下走。
  2. 关注字符串解密函数:如果看到类似“decryptString”或“getString”的方法,八成是解密函数。把它单独拎出来,写个脚本跑一遍,还原所有字符串。
  3. 利用JADX的反混淆插件:JADX有个“Deobfuscation”插件,能自动重命名混淆类。虽然效果一般,但至少能把“a”、“b”改成“Class1”、“Class2”,方便阅读。

核心思路:混淆只是增加了阅读难度,没有改变代码的逻辑。只要你能找到关键的数据流和控制流,混淆就是一层窗户纸。

5.3.2 实战:反混淆一个字符串加密函数

假设我们在JADX里看到这样的代码:

public static String a(String str) {
    char[] cArr = str.toCharArray();
    for (int i = 0; i < cArr.length; i++) {
        cArr[i] = (char) (cArr[i] ^ 0x7F);
    }
    return new String(cArr);
}

这明显是一个异或解密函数。我们只需要找到所有调用“a”的地方,把加密后的字符串提取出来,然后用同样的算法解密。我一般写个Python脚本:

def decrypt(s):
    return ''.join(chr(ord(c) ^ 0x7F) for c in s)

# 从JADX里复制出来的加密字符串
encrypted = ["\u0080\u0081\u0082", "\u0083\u0084\u0085"]
for e in encrypted:
    print(decrypt(e))

你看,几行代码就把所有字符串还原了。这就是反混淆的入门操作。

5.4 知识体系图

下面这张图,是我自己总结的静态分析知识体系。你可以把它当作一个检查清单,看看自己掌握了哪些。

静态分析工具实战 JADX • 全局搜索定位 • 反混淆开关 • 导出Gradle项目 • 继承链分析 GDA • 交叉引用追踪 • 寄存器分析 • 控制流图(CFG) • DEX编辑器 代码混淆与反混淆 • 名称混淆 → 看继承链 • 字符串加密 → 写脚本解密 • 控制流混淆 → 用CFG分析

这张图里,JADX和GDA是左右护法,混淆对抗是核心目标。你不需要一次性掌握所有功能,但至少要知道:遇到什么场景,该用哪个工具。

5.5 避坑指南

最后,分享几个我踩过的坑:

  • 别迷信自动化反混淆:我曾经用某个工具一键反混淆,结果把正常的类名也改了,导致代码完全不可读。手动分析+脚本辅助,才是正道。
  • 注意DEX版本:JADX和GDA对Android 10以上的新DEX格式支持不够好。如果遇到解析失败,试试用Android SDK里的d8工具重新打包。
  • 别忽略资源文件:很多关键信息藏在AndroidManifest.xml或res/values/strings.xml里。比如,我曾在strings.xml里找到过硬编码的API密钥。
一句话总结:静态分析的核心不是工具,而是思路。先找入口,再追数据流,最后用脚本辅助。工具只是加速这个过程。

公众号:蓝海资料掘金营,微信deep3321