Oo0代码混淆实现方法
看过 MT 的 dex 文件的朋友们肯定都发现了它的所有类名、方法名、字段名都成了 Oo0 的各种混合体,这虽然不能起很好的保护作用,但还是可以恶心一下反编译你软件的人。
最开始我是通过给 Proguard 设置字典的方式来实现这个效果,也就是提前生成 N 个不同的 Oo0 组合保存到 txt 文件,然后在混淆配置中设置下即可。
不过这个方法存在一些局限性,首先你要手动生成字典文件,不能在每次混淆时随机生成名字,其次你要确保字典名字中数量要够用,否则又会变回 abcd 的组合了。
另外 Android Studio 默认的 Pruguard 配置是不区分名称大小写,也就是像 OO|Oo|oO|oo 会被过滤得只剩下一个,其它三个由于在不区分大小写的情况下是相同的被删掉了。
最后我打起了修改 Proguard 源码的主意。
首先我们先去看看 Android Studio 使用的 Proguard 版本,Mac 上是在 Finder 的应用程序中找到 Android Studio,右键-显示包内容,接着进入
Contents/gradle/m2repository/net/sf/proguard/proguard-base
Windows 版本如果我没记错是先进入 Android Studio 的安装目录,然后再进入
gradle/m2repository/net/sf/proguard/proguard-base
最后你可以看到下面几个文件夹:
最高版本是5.3.3,所以现在我们去下载 Proguard-5.3.3 的源码,网址在 https://sourceforge.net/projects/proguard/files/proguard/
然后我们用 Intellij IDEA 来创建个 Java 项目,就取名 Oo0Proguard,创建完成后,解压 Proguard 的源码,把 src 文件夹复制过来。
由于我们只需要编译 proguard-base.jar 包给 Android Studio 使用,所以删掉先一些不需要的源代码,分别为下面五个包:
proguard/ant
proguard/gradle
proguard/gui
proguard/retrace
proguard/wtk
同时把 proguard/MANIFEST.MF 也删掉,最终效果:
现在来配置一下项目的 Artifacts,用来将源码打包成 jar。点击菜单 File - Project Structrue,然后如下图操作:
重命名输出的 jar 文件名为 proguard-base-5.3.3,操作如下:
最后点击 OK 保存设置。我们再点击菜单 Build - Build Artifacts - Oo0Projuard:jar - Build,然后就可以看到打包好的 jar 了。
OK,到这边我们完成了第一步,可以编译出原版的 proguard-base.jar 了。
现在开始对 Proguard 进行修改。我研究了半天源码才找到下面这些修改位置的,具体过程就不多说了,直接给你们。
修改版本名称
具体在 proguard.Proguard.VERSION,这样方便我们在 Android Studio 的编译日志中确定是否调用了我们修改的 Proguard。
强制名称混合大小写
Android Studio 默认的 Proguard 配置中有一条
-dontusemixedcaseclassnames
可以修改源码忽略掉这条规则。
定位到 proguard/obfuscate/ClassObfuscator.java 的 125 行
this.useMixedCaseClassNames = useMixedCaseClassNames;
修改为
this.useMixedCaseClassNames = true;
强制不保留内部类
Android Studio 默认的 Proguard 配置中有一条
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod
其中 InnerClasses 和 EnclosingMethod 会导致混淆后可能出现 Oo0$1 之类的名称,可以修改源码忽略掉这两个值。
打开 proguard/ConfigurationParser.java,搜索KEEP_ATTRIBUTES_OPTION,给该行的 if 语句加对大括号,然后添加代码删掉 configuration.keepAttributes 内的 InnerClasses 和 EnclosingMethod,如下图,红色框出来的是添加的代码。
创建Oo0名称工厂
具体代码放在 Github 上,在 src/proguard/obfuscate/ONameFactory.java,其中我使用了 Random 来让每次运行生成的名称顺序不一样。
使用Oo0名称工厂
打开 proguard/obfuscate/ClassObfuscator.java,把全部
new SimpleNameFactory(useMixedCaseClassNames)
替换为
new ONameFactory()
打开 proguard/obfuscate/Obfuscator.java,把全部
new SimpleNameFactory()
替换为
new ONameFactory()
如果后续的 Proguard 版本出现变化,你们搜一下 SimpleNameFactory 的引用,把除了出现在 main 方法中的,其它全部都替换掉就行了。
修改源文件名
我们在编写应用时总难免会出现 bug,当程序奔溃后,主要是通过异常堆栈、源文件名、行号来定位代码,由于类名和源文件名基本一样,既然我们混淆了类名,自然也不希望源文件名暴露出去。
第一种方法是通过 Proguard 来删除源文件名,这样奔溃日志中也是看不到源文件名的,我们可以通过混淆时生成的 mapping.txt 文件来恢复。但使用这种方法的话,你需要保存各个版本的 mapping.txt 文件,相当不方便。
第二种方法是对源文件名进行加密,这样我们在奔溃日志中看到的是加密后的名称,只需要解密一下就行了,直接上修改方法:
定位到 proguard.obfuscate.SourceFileRenamer 的 visitSourceFileAttribute 方法,将方法中的全部代码替换为
String sourceFile = clazz.getString(sourceFileAttribute.u2sourceFileIndex);
if (!(clazz.getName() + ".java").endsWith(sourceFile))
sourceFile = encrypt(sourceFile);
sourceFileAttribute.u2sourceFileIndex =
new ConstantPoolEditor((ProgramClass) clazz).addUtf8Constant(sourceFile);
其中的 if 判断是过滤掉类名没变化的类,例如所有 Activity 的类名是不会被混淆的,其源文件名自然也不需要加密。encrypt 方法用于加密字符串,具体实现由你们自行设计。
然后我们在定位到 SourceFileRenamer 的唯一调用处,在 proguard/obfuscate/Obfuscator.java 的 467 行,把 if 判断注释掉。
到这边我们已完成了所有的修改,再次点击菜单 Build - Build Artifacts - Oo0Projuard:jar - Build,编译出 proguard-base-5.3.3.jar 文件。
现在来替换 Android Studio 的 Proguard,在这之前记得先退出 Android Studio,然后回到我们刚刚打开的目录
打开 5.3.3,把 proguard-base-5.3.3.jar 重命名为 proguard-base-5.3.3_bak.jar,再把我们编译好的 proguard-base-5.3.3.jar 复制进来。注意,当我们通过补丁升级 Android Studio 前需要还原文件,否则无法升级。
现在我们再次打开 Android Studio,打开项目,进入 app 下的 build.gradle,把 minifyEnabled 设为 true,开启 Proguard 混淆功能。
然后再打开 proguard-rules.pro 混淆配置文件,加入下面的内容:
# 修改包名
-repackageclass ""
# 忽略访问修饰符,配合上一句使用
-allowaccessmodification
# 不要删除源文件名和行号
-keepattributes SourceFile,LineNumberTable
配置完成,编译发行版的 APK,然后点击右下角的 Gradle Console,当开始混淆时会看到一行 ProGuard, version 5.3.3(Oo0),这是我修改后的版本名称,说明我修改的 Proguard 已经成功被 Android Studio 调用了。
编译完成后,点击菜单 Build- Analyze APK,打开我们刚刚编译好的 apk 文件,然后点击 classes.dex,就可以看到类名、方法名、字段名都被混淆成了各种 Oo0 了。
把 apk 复制到手机,使用 MT 打开里面的 classes.dex 文件,随便打开一个类,可以看到源文件名也更改成功了,这边我没有加密,只是在后面加了 -encrypt
全部代码已上传到 Github
https://github.com/L-JINBIN/Oo0Proguard
也可以点击下面的阅读原文查看