[![](https://jitpack.io/v/liujingxing/XmlClassGuard.svg)](https://jitpack.io/#liujingxing/XmlClassGuard) [![](https://img.shields.io/badge/change-更新日志-success.svg)](https://github.com/liujingxing/XmlClassGuard/wiki) [![](https://img.shields.io/badge/FAQ-常见问题-success.svg)](https://github.com/liujingxing/XmlClassGuard/wiki/FAQ) [![](https://img.shields.io/badge/掘金-@刘一刀-blue.svg)](https://juejin.cn/user/272334612601559/posts) # TODO (欢迎提PR) - 混淆xml文件中用到的系统类,如TextView/Button等,用自定义View替代 # XmlClassGuard 简介 - `XmlClassGuard`是一个可混淆Android 4大组件、自定义View等任意类的插件 - `XmlClassGuard`可以看作是`ProGuard`的一个补充,跟`ProGuard`没有任何关系,也不会有任何冲突 - 可快速更改`manifest`文件里的`package`属性,并同步到其他文件中 - 可快速移动n个目录到其他目录中,并同步到其他文件中 - 可查找`constraint_referenced_ids`属性的值,并自动添加到`AabResGuard`的白名单中 - 可查找`constraint_referenced_ids`属性的值,并自动添加到`AndResGuard`的白名单中 - `XmlClassGuard`最主要的功能是混淆xml文件用到的类,故取名为`XmlClassGuard`,与[AndResGuard](https://github.com/shwenzhang/AndResGuard)、[AabResGuard](https://github.com/bytedance/AabResGuard)对应 # 有什么用? - 弥补`ProGuard`不混淆4大组件等类问题 - 增加aab、apk反编译的难度 - 极大降低aab包查重率,避免上架`Google Play`因查重率过高,导致下架或封号问题 关于第三点,有过上架`Google Play` 商店的同学应该知道,如果之前的包被下架或封号,想要同套代码再次上架,那99%概率是再次封号,很大一部分原因就是以上说到的类未被混淆,很容易被Google断定为包重复,从而导致再次封号,因此,如果想要再次上架,就必须要更改四大组件、自定义View等的`包名+类名`以降低查重率,然而,如果手动去完成这项任务,估计会累死一个程序员,于是乎,就有了`XmlClassGuard`,通过插件去完成手工的活,一个任务便可搞定 # 原理 `XmlClassGuard`不同于`AndResGuard(apk资源混淆)、AadResGuard(aab资源混淆)`侵入打包流程的方案,`XmlClassGuard` 需要在打包前执行`xmlClassGuard`任务,该任务会检索`AndroidManifest.xml`及`navigation、layout、xml` 文件夹下的xml文件,找出xml文件中引用的类,如4大组件及自定义View等,更改其`包名+类名`,并将更改后的内容同步到其他文件中,说直白点,就是在打包前,在本地更改`包名+类名` # 警告警告⚠️ **由于是在本地操作,任务执行是不可逆的,故务必做好代码备份,否则代码将很难还原** # 上手 1、在`build.gradle(root project)`中配置 ```gradle buildscript { repositories { maven { url 'https://jitpack.io' } } dependencies { classpath "com.github.liujingxing:XmlClassGuard:1.2.6" } } ``` 2、在 `build.gradle(application)` 中配置 ```gradle apply plugin: "xml-class-guard" //以下均为非必须 xmlClassGuard { /* * 是否查找约束布局的constraint_referenced_ids属性的值,并添加到AabResGuard的白名单中, * 是的话,要求你在XmlClassGuard前依赖AabResGuard插件,默认false */ findAabConstraintReferencedIds = false /* * 是否查找约束布局的constraint_referenced_ids属性的值,并添加到AndResGuard的白名单中, * 是的话,要求你在XmlClassGuard前依赖AndResGuard插件,默认false */ findAndConstraintReferencedIds = false //用于增量混淆的 mapping 文件 mappingFile = file("xml-class-mapping.txt") //更改manifest文件的package属性,即包名 packageChange = ["com.ljx.example": "ab.cd"] //移动目录 moveDir = ["com.ljx.example": "ef.gh"] } ``` 此时就可以在`Gradle`栏中,找到以下4个任务 ![image.png](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/7e98f0285572474182947cdfdc7c064d~tplv-k3u1fbpfcp-watermark.image?) # 任务介绍 `XmlClassGuard`插件共有5个任务,分别是`findAabConstraintReferencedIds`、`findAndConstraintReferencedIds`、`moveDir`、`packageChange`及`xmlClassGuard`,这5个任务之间没有任何关系,下面将一一介绍 ## 1、findAabConstraintReferencedIds/findAndConstraintReferencedIds `findAabConstraintReferencedIds`需配合[AabResGuard](https://github.com/bytedance/AabResGuard)插件使用,如果你未使用AabResGuard插件,可忽略。 `findAndConstraintReferencedIds`需配合[AndResGuard](https://github.com/shwenzhang/AndResGuard)插件使用,如果你未使用AndResGuard插件,可忽略。 下面仅介绍`findAabConstraintReferencedIds`任务,`findAndConstraintReferencedIds`任务同理 由于约束布局`constraint_referenced_ids`属性的值,内部是通过getIdentifier方法获取具体的id,这就要求我们把`constraint_referenced_ids`属性的值添加进`AabResGuard`的白名单中,否则打包时,id会被混淆,打包后,`constraint_referenced_ids`属性会失效,UI将出现异常。 然而,项目中可能很多地方都用到`constraint_referenced_ids`属性,并且值非常多,要一个个找出来并手动添加到`AabResGuard`的白名单中,无疑是一项繁琐的工作,于是乎,`findAabConstraintReferencedIds`任务就派上用场了,它是在打包时,自动查找`constraint_referenced_ids`属性并添加进`AabResGuard`的白名单中,非常实用的功能,你仅需要在`XmlClassGurad`的配置`findAabConstraintReferencedIds`为true即可,如下: ```gradle //以下均为非必须 xmlClassGuard { /* * 是否查找约束布局的constraint_referenced_ids属性的值,并添加到AabResGuard的白名单中, * true的话,要求你在XmlClassGuard前依赖AabResGuard插件,默认false */ findAabConstraintReferencedIds = true } ``` `findConstraintReferencedIds`任务不需要手动执行,打包(aab)时会自动执行 ## 2、moveDir `moveDir`是一个移动目录的任务,它支持同时移动任意个目录,它会将原目录下的所有文件(包括子目录)移动到另外一个文件夹下,并将移动的结果,同步到其他文件中,配置如下: ```gradle xmlClassGuard { //移动目录 moveDir = ["com.ljx.example": "ef.gh", "com.ljx.example.test": "ff.gg"] } ``` 上面代码中`moveDir`是一个Map对象,其中key代表要移动的目录,value代表目标目录; 上面任务会把`com.ljx.example`目录下的所有文件,移动到`ef.gh` 目录下,将`com.ljx.example.test`目录下的所有文件移动到`ff.gg`目录下 ## 3、packageChange `packageChange`是一个更改`manifest`文件里`package`属性的任务,也就是更改app包名的任务(不会更改applicationId) ,改完后,会将更改结果,同步到其他文件中(不会更改项目结构),配置如下: ```gradle xmlClassGuard { //更改manifest文件的package属性,即包名 packageChange = ["com.ljx.example": "ab.cd"] } ``` 以上`packageChange`是一个Map对象,key为原始package属性,value为要更改的package属性,原始package属性不匹配,将更改失败 ## 4、xmlClassGuard `xmlClassGuard`是一个混淆类的任务,该任务会检索`AndroidManifest.xml`及`navigation、layout` 文件夹下的xml文件,找出xml文件中引用到的类,如4大组件及自定义View等,更改其`包名+类名`,并将更改的结果,同步到其他文件中,最后会将混淆映射写出到mapping文件中,配置如下: ```gradle xmlClassGuard { //用于增量混淆的 mapping 文件 mappingFile = file("xml-class-mapping.txt") } ``` 上面配置的`mappingFile`可以是一个不存在的文件,混淆结束后,会将混淆映射写出到该文件中,如下: ```xml dir mapping: com.ljx.example -> e com.ljx.example.activity -> dh class mapping: com.ljx.example.AppHolder -> e.B com.ljx.example.activity.MainActivity -> dh.C ``` `dir mapping`是混淆的目录列表,`class mapping`是具体类的混淆列表 ## 5、混淆任意类 `xmlClassGuard`任务是支持增量混淆的,如果你需要混淆指定的类`com.ljx.example.test.Test`,便可以在`dir mapping`下写入`com.ljx.example.test -> h`, 此时再次执行`xmlClassGuard`任务,便会将`com.ljx.example.test`目录下的所有类(不包含子目录下的类) 移动到`h`文件夹中,并将所有类名混淆,再次混淆的后mapping文件如下: ```xml dir mapping: com.ljx.example -> e com.ljx.example.activity -> dh com.ljx.example.test -> h class mapping: com.ljx.example.AppHolder -> e.B com.ljx.example.activity.MainActivity -> dh.C com.ljx.example.test.Test -> h.D ``` 手动输入混淆规则,需要注意以下几条规则 ![mapping_rule.jpg](https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/32f446561004426193dae50537488180~tplv-k3u1fbpfcp-watermark.image?) ## 6、每次混淆产生不一样的结果 默认情况下,每次混淆,都将产生一样的结果,混淆的包名根据哈希算法得出,混淆的类名,从大写字母A开启,依次递增,如:`A B C ... Y Z BA BB .. ZY ZZ BAA...` 如果你需要每次混淆产生不一样的结果,只需做两步 - 对于包名,需要你配置每一个 - 对于类名,可以每一个都去配置,但类太多时,配置每一个,就显得繁琐,此时仅需要配置一个即可 如我们修改一下上面的`mapping`文件,如下 ```xml dir mapping: com.ljx.example -> hh com.ljx.example.activity -> jk com.ljx.example.test -> et class mapping: com.ljx.example.AppHolder -> hh.Z ``` 此时执行`xmlClassGuard`任务,就会产生不一样的结果,如下: ```xml dir mapping: com.ljx.example -> hh com.ljx.example.activity -> jk com.ljx.example.test -> et class mapping: com.ljx.example.AppHolder -> hh.Z com.ljx.example.activity.MainActivity -> jk.BA com.ljx.example.test.Test -> et.BC ``` 可以看到,包名完全是根据自定义生成的结果,而类名便从`Z`开始,依次递增`Z BA BC ...`, 这里可以把类名看成26进制的字符串依次递增 # 注意事项⚠️ - 要混淆的类,要避免与其他类同名,否则类名替换时,会出现误杀情况 - 类混淆后,类的包名(路径)也会被混淆,所以,如果你用到一些三方库,有配置包名的地方,记得手动更改 - `XmlClassGuard`不会更改`proguard-rules.pro`文件的内容,所以,类混淆后,如果该文件内容有混淆前的类或目录,也记得手动更改 - `XmlClassGuard`只会帮你更改`包名+类名`,并同步到其它文件中,不会更改你的任何代码逻辑,如混淆后,出现部分功能不正常问题,需要你自己查找原因,如果是`XmlClassGuard`的问题,欢迎提[issue](https://github.com/liujingxing/XmlClassGuard/issues)或[PR](https://github.com/liujingxing/XmlClassGuard/pulls) ## Donations 如果它对你帮助很大,并且你很想支持库的后续开发和维护,那么你可以扫下方二维码随意打赏我,就当是请我喝杯咖啡或是啤酒,开源不易,感激不尽 ![donations.png](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fa6d3941c2c944e59831640fa0ece60d~tplv-k3u1fbpfcp-watermark.image?)