2

简单几行代码给你的android应用生成更难以阅读的混淆字典

 2 years ago
source link: https://yrom.net/blog/2019/06/19/simple-codes-to-generate-obfuscation-dictionary/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

简单几行代码给你的android应用生成更难以阅读的混淆字典

2019-06-19 Android

普遍的,不论大小Android应用都会配置 proguard 在release 编译的时候混淆自己的代码:

android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

但无论 Proguard 还是 R8,他们的混淆字典默认都太简单了(too simple),只是 abcdefg 而已,反编译后还是很容易阅读的,如下所示:

final class b {
Object a;
aes b;
ada c;

b() {
}
}

所幸,Proguard 支持自定义字典:

-obfuscationdictionary dict.txt
-classobfuscationdictionary dict.txt
-packageobfuscationdictionary dict.txt

如果,有那么一个字典,里面都是形似“乱码”的字符,看起来不仅费眼睛,甚至电脑字体还没收录更佳(会显示成一个方框)。

万能的github 上还真有符合要求的。但是直接生成好的字典文件一直用也是有隐患的,举个例子,两个版本之间类、方法的个数差别不大,最终的混淆结果其实是很相似的,对比 mapping 之后,有可能一个方法前一个版本叫 aa,现在叫 ab 了。

而且翻看了部分实现方案,要么是字典文件里词汇量不够大,要么生成代码实现可能有其它bug。故而干脆自己撸起袖子几行代码搞定。

首先给 app build.gradle 加一个生成字典的任务:

task genDict {
outputs.file('build/tmp/dict.txt')
doLast {
def r = new Random()
def start = r.nextInt(1000) + 0x0100
def end = start + 0x4000 // 如果字典太大了,可以将start~end范围缩小
def chars = (start..end)
.findAll { Character.isValidCodePoint(it) && Character.isJavaIdentifierPart(it) }
.collect { String.valueOf(Character.toChars(it)) }
int max = chars.size()
def startChars = []
def dict = []
// 筛选可用作java标识符开头的char
for (int i = 0; i < max; i++) {
char c = chars.get(i).charAt(0)
if (Character.isJavaIdentifierStart(c)) {
startChars << String.valueOf(c)
}
}
def startSize = startChars.size()
// 打乱顺序
Collections.shuffle(chars, r)
Collections.shuffle(startChars, r)
// 拼两个char为一个词,让字典更丰富
for (int i = 0; i < max; i++) {
def m = r.nextInt(startSize - 3)
def n = m + 3
(m..n).each { j ->
dict << (startChars.get(j) + chars.get(i))
}
}

def f = outputs.files.getSingleFile()
f.getParentFile().mkdirs()
f.withWriter("UTF-8") {
it.write(startChars.join(System.lineSeparator()))
it.write(dict.join(System.lineSeparator()))
}
}
}

手动执行./gradlew genDict,看看字典,都是这种字符:









چ







Ī


自动生成proguard字典也很简单:

afterEvaluate {
// 只给 release 包注册
android.applicationVariants.all { variant ->
if (variant.name.endsWith('Release'))
variant.javaCompileProvider.configure {
dependsOn 'genDict'
}
}
}

最后别忘了在 proguard rules 里配置字典文件

-obfuscationdictionary build/tmp/dict.txt
-classobfuscationdictionary build/tmp/dict.txt
-packageobfuscationdictionary build/tmp/dict.txt

混淆后效果:

class Ƞ extends 칁<홳> {
public final 驀 치;

public Ƞ(驀 驀) {
this.치 = 驀;
}

public void 치(ꃑ ꃑ) {
}

public void 치(홳 홳) {
this.치.逥 = true;
}
}

对了,android gradle plugin 的版本是 3.4.1,gradle 版本 5.1.1,如果你用的和我不一样,前面所提到的代码(genDict)可能需要稍作修改。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK