Android 混淆字典完全指南:R8 时代还能用吗?11 套字典实测对比

Android 混淆字典完全指南:R8 时代还能用吗?11 套字典实测对比

开启 ProGuard/R8 混淆后,你的类名、方法名、字段名会被重命名为 abc 这样的短名称。但这套默认命名方案有两个问题:一是太规律,逆向工具一眼就能识别这是混淆后的代码;二是所有项目的混淆结果都长一样,没有任何个性化。

混淆字典 (Obfuscation Dictionary)就是用来解决这个问题的------你可以指定一组自定义字符来替代默认的 a-z,让混淆后的名称变成视觉上难以辨认的 Unicode 字符、Java 关键字、甚至跨编码乱码。

但有一个很多人忽略的问题:从 AGP 3.4 起,Android 默认的混淆器已经从 ProGuard 切换到了 R8。那 -obfuscationdictionary 这个 ProGuard 时代的配置,R8 还支持吗?

这篇文章会从 R8 兼容性、字典配置方法、11 套字典策略对比、验证流程这几个角度,把混淆字典这件事讲透。

一、R8 还支持混淆字典吗?

先给结论:支持。

R8 在设计上兼容 ProGuard 的规则文件(proguard-rules.pro),包括混淆字典相关的三个配置项。R8 源码中有专门的 DictionaryReader 类(位于 com.android.tools.r8.naming 包),负责解析字典文件并应用到命名流程中。

在 R8 Full Mode(AGP 8.0+ 默认开启)的不兼容特性列表中,列出的不支持项主要是 -whyareyoukeeping、枚举合并、类合并、参数移除等优化相关功能。混淆字典属于命名(naming)阶段,不在不兼容列表内。

简单说:ProGuard 时代的混淆字典配置,在 R8 下可以原样使用,不需要做任何改动。

二、三个字典配置项

ProGuard/R8 提供了三个独立的字典配置,分别控制不同层级的命名:

proguard 复制代码
# 字段名 + 方法名混淆字典
-obfuscationdictionary proguard-dic-homoglyph.txt

# 类名混淆字典
-classobfuscationdictionary proguard-dic-homoglyph.txt

# 包名混淆字典
-packageobfuscationdictionary proguard-dic-homoglyph.txt

三个配置可以指向同一个字典文件,也可以分别使用不同的字典。

字典文件格式 :纯文本,每行一个词。以 # 开头的行为注释,会被忽略。空白行和重复项也会被自动过滤。字典中的字符必须是合法的 Java 标识符起始字符(通过 Character.isJavaIdentifierStart() 校验),否则 R8 会跳过该条目。

字典耗尽机制 :如果字典中的字符用完了(比如字典只有 10 个字符,但项目有 100 个类需要命名),R8 会用字典字符做组合(类似 aaabac...)。如果组合也耗尽了,会回退到默认的 a-z 命名。所以字典的字符数量直接决定了混淆效果的覆盖范围。

三、字典策略对比

我整理了 11 套混淆字典,按策略分为四类。下面是完整对比:

3.1 形似字符类(Homoglyph)

核心思路是用不同 Unicode 区段中视觉上相似 的字符来替代 ASCII 字母。比如希腊字母的 α(U+03B1)和拉丁字母 a 在很多字体中几乎一样,西里尔字母的 а(U+0430)也是。

逆向人员用 jadx 打开后看到的方法名可能是 аbс------看起来像 abc,但实际是三个来自不同语系的字符。搜索、复制粘贴、代码分析工具全部失效。

字典 字符数 3 字符组合数 说明
Homoglyph 255 16,581,375 跨语系视觉形似字符,覆盖面最广
Minimal Confusable 52 140,608 每个 ASCII 字母取一个最佳替代,轻量方案

Homoglyph 是我最常用的,字符集适中,组合空间大,反编译后的视觉混淆效果最好。Minimal Confusable 适合不想太激进的场景,只做最小替换。

3.2 特定语系类

使用某个完整语系的字符集。逆向人员即使知道用了哪个语系,面对满屏不认识的字符也很难手动分析。

字典 字符数 3 字符组合数 说明
Greek 122 1,815,848 希腊字母及扩展形式
Cyrillic 296 25,934,336 西里尔字母(俄语等使用)
Latin Extended 494 120,553,784 拉丁扩展,含变音符号、IPA
Hebrew + Arabic 423 75,686,967 希伯来语 + 阿拉伯语,含从右到左书写方向
East Asian 246 14,886,936 CJK 部首、片假名、平假名
Indigenous Scripts 1,146 1,505,060,136 切罗基、格鲁吉亚、亚美尼亚、泰语、老挝语等

Hebrew + Arabic 有个额外效果:这些语系是从右到左书写的,在部分 IDE 和反编译工具中会导致光标移动和文本选择异常,进一步增加逆向难度。

Indigenous Scripts 的字符数最多(1,146),3 字符组合超过 15 亿,适用于超大项目。

3.3 编码干扰类

字典 字符数 3 字符组合数 说明
Encoding Chaos 575 190,109,375 UTF-8 字节在 GBK/Big5/Shift-JIS 下解码为完全不同的字符

这套字典的思路比较独特:选取的字符在 UTF-8 编码下是合法的,但如果逆向人员的工具或终端用 GBK、Big5、Shift-JIS 等编码打开,会看到完全不同的乱码。这在中文开发环境中尤其有效,因为很多工具的默认编码不是 UTF-8。

3.4 全量合并类

字典 字符数 3 字符组合数 说明
Ultimate 3,056 28,540,399,616 所有语系合并,最大字符集

如果不确定选哪个,直接用 Ultimate。3,056 个字符,3 字符组合超过 285 亿,覆盖任何规模的项目。

四、实际配置

在 Android 项目中使用混淆字典只需要两步。

4.1 放置字典文件

把下载的字典文件(.txt)放到项目根目录或 proguard-rules.pro 同级目录下:

复制代码
app/
├── proguard-rules.pro
├── proguard-dic-homoglyph.txt   ← 字典文件
└── build.gradle.kts

4.2 在 ProGuard 规则中引用

proguard-rules.pro 中添加:

proguard 复制代码
# 混淆字典配置(R8 兼容)
-obfuscationdictionary proguard-dic-homoglyph.txt
-classobfuscationdictionary proguard-dic-homoglyph.txt
-packageobfuscationdictionary proguard-dic-homoglyph.txt

这三行就够了。R8 在构建时会自动读取字典文件并应用到命名流程。

4.3 推荐的字典组合

根据项目需求选择:

  • 通用推荐Homoglyph --- 视觉混淆效果最好,字符集大小适中
  • 最大混淆Ultimate --- 3,056 字符,适合大型项目
  • 轻量方案Minimal Confusable --- 52 字符,对 APK 体积影响最小
  • 编码干扰Encoding Chaos --- 适合面向中文用户的应用,利用 GBK 编码差异

这 11 套字典都可以在 codetroupe.github.io/dictionarie... 免费下载,所有字符都经过 Character.isJavaIdentifierStart() 校验,可以直接使用。

五、验证字典是否生效

配置完字典后,需要验证它是否真的生效了。三个步骤:

5.1 检查 mapping.txt

构建完成后,查看 app/build/outputs/mapping/release/mapping.txt。如果字典生效,你会看到类似这样的映射:

csharp 复制代码
com.example.MyClass -> αβγ.αβ:
    void myMethod() -> αα(αβγ)

如果还是 a.b.c 这种默认命名,说明字典没有被正确加载,检查文件路径是否正确。

5.2 反编译验证

用 jadx 打开混淆后的 APK/AAB,直接看类名和方法名。好的字典会让反编译结果变成这样:

java 复制代码
// 使用 Homoglyph 字典后的效果
public class αβγ {
    public static void аbс(аβγ var0) {
        // 方法名看起来像 abc 但实际是希腊+西里尔字符
    }
}

对比默认混淆:

java 复制代码
// 默认混淆效果
public class a {
    public static void a(a var0) {
        // 规律的 a, b, c 命名
    }
}

5.3 字典耗尽检查

如果项目很大(数千个类),检查 mapping.txt 的尾部,看是否出现了回退到默认 a-z 命名的情况。如果出现了,说明字典的字符组合空间不够,需要换用字符数更多的字典(比如从 Minimal Confusable 换到 HomoglyphUltimate)。

六、对 APK 体积的影响

使用 Unicode 字符替代 ASCII 会增加 DEX 文件中的字符串池大小,因为 UTF-8 编码下一个中文字符占 3 字节,而 ASCII 字符只占 1 字节。

但实际影响很小。以一个中等规模项目为例:

方案 DEX 字符串池增量 占 APK 比例
默认 a-z 基准 -
Homoglyph(255 字符) +2~4 KB < 0.1%
Ultimate(3,056 字符) +8~15 KB < 0.2%
Encoding Chaos(575 字符) +4~8 KB < 0.1%

对于现代 Android 应用(通常 20-50 MB),这点增量完全可以忽略。R8 的 minifyEnabled 本身就会做大量优化来缩减 DEX 体积,字典带来的几 KB 增量不构成问题。

七、混淆字典的局限性

需要诚实说明的一点:混淆字典不会从根本上提升代码安全性。

Guardsquare(ProGuard 的开发商)在官方文档中也提到,混淆字典"hardly improves the obfuscation"------它只是增加反编译后代码的可读性难度,而不是阻止反编译本身。有经验的逆向工程师仍然可以通过以下方式绕过:

  • 查看 mapping.txt(如果你没有保护好这个文件)
  • 用 JADX 的"Deobfuscate"功能自动还原
  • 通过字符串常量、API 调用模式等上下文信息推断功能

混淆字典的真正价值在于提高逆向的时间成本。对于大部分脚本小子和初级逆向人员来说,满屏的 Unicode 形似字符足以让他们放弃。而对于专业逆向人员,你需要配合 R8 Full Mode 的激进优化、字符串加密、反调试等手段来构建多层防护。

八、常见问题

Q:R8 Full Mode 下字典配置会失效吗? 不会。Full Mode 的不兼容列表中没有字典相关选项。R8 Full Mode + 混淆字典的组合在 AGP 8.x 下可以正常工作。

Q:字典文件的路径怎么写? 相对于 proguard-rules.pro 文件的路径。如果字典文件和规则文件在同一目录,直接写文件名即可。

Q:三个字典配置必须用同一个文件吗? 不必须。你可以给类名用一个字典,给方法名用另一个。但通常用同一个就够了。

Q:字典字符会不会导致编译错误? 不会。只要字符通过了 Character.isJavaIdentifierStart() 校验(上面提供的 11 套字典都已校验),就不会有编译问题。

Q:混淆字典和 -dontobfuscate 冲突吗? 冲突。-dontobfuscate 会禁用整个混淆步骤,字典配置自然也不会生效。确保你的 release 构建没有设置 -dontobfuscate

Q:debug 构建也会应用字典吗? 取决于你的 ProGuard 配置是否在 debug buildType 中启用。通常 debug 不启用混淆,所以字典也不会生效。

九、写在最后

混淆字典是一个成本低、效果直观的混淆增强手段。三行配置、一个文本文件,就能让反编译结果从一目了然变成满屏天书。虽然它不能替代专业的代码保护方案,但作为混淆链路上的一层加固,性价比很高。

如果你用的是 JetBrains IDE,ADB Pro 插件的 R8 Assistant 内置了字典管理功能,可以直接在 IDE 中预览、下载和应用这 11 套字典,不需要手动管理文件。更多关于混淆字典和 R8 配置的内容,也可以参考 ADB Pro 文档

相关推荐
課代表7 个月前
PowerShell 字符转 UniCode 编码
字符编码·类型转换·unicode·powershell·批处理·转义·[char]
无心水7 个月前
【Python实战进阶】5、Python字符串终极指南:从基础到高性能处理的完整秘籍
开发语言·网络·python·字符串·unicode·python实战进阶·python工业化实战进阶
力江8 个月前
攻克维吾尔语识别的技术实践(多语言智能识别系统)
人工智能·python·自然语言处理·语音识别·unicode·维吾尔语
liulilittle10 个月前
UTF-8 编解码可视化分析
c++·字符串·unicode·string·字符·char·utf8
imred1 年前
Unicode:如何让用户东方不败和[Family: Man, Woman, Girl, Boy]顺利通过用户名长度检查?
c++·unicode·utf8·icu
吴八月1 年前
字符集、编码的前世今生
utf-8·unicode·字符集编码
dingdingfish1 年前
Oracle NLS_LANG 常见问题
oracle·database·unicode·utf·globalization
初级代码游戏1 年前
源码:处理文件格式和字符集的相关代码(3-3)
字符编码·乱码·代码页·unicode·bom·utf·文本格式
一丝晨光1 年前
如何很快将文件转换成另外一种编码格式?编码?按指定编码格式编译?如何检测文件编码格式?Java .class文件编码和JVM运行期内存编码?
java·c++·python·visual studio·unicode·ansi·utf8