代码混淆
(Obfuscated code)亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为。代码混淆可以用于程序源代码,也可以用于程序编译而成的中间代码。执行代码混淆的程序被称作代码混淆器。已经存在许多种功能各异的代码混淆器。
将代码中的各种元素,如变量,函数,类的名字改写成无意义的名字。比如改写成单个字母,或是简短的无意义字母组合,甚至改写成“__”这样的符号,使得阅读的人无法根据名字猜测其用途。重写代码中的部分逻辑,将其变成功能上等价,但是更难理解的形式。比如将for循环改写成while循环,将循环改写成递归,精简中间变量,等等。打乱代码的格式。比如删除空格,将多行代码挤到一行中,或者将一行代码断成多行等等。
代码混淆器也会带来一些问题。主要的问题包括:
被混淆的代码难于理解,因此调试以及除错也变得困难起来。开发人员通常需要保留原始的未混淆的代码用于调试。对于支持反射的语言,代码混淆有可能与反射发生冲突。代码混淆并不能真正阻止反向工程,只能增大其难度。因此,对于对安全性要求很高的场合,仅仅使用代码混淆并不能保证源代码的安全。
Android混淆:常见的不参与
1.系统文件不参与混淆
2.反射不参与混淆
3.注解不参与混淆
4.EventBus不参与混淆
5.官方包下的类不参与混淆
常见不参与混淆需要keep下来的:
-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
-ignorewarnings # 忽略警告,避免打包时某些警告出现
-optimizationpasses 5 # 指定代码的压缩级别
-dontusemixedcaseclassnames # 是否使用大小写混合
-dontskipnonpubliclibraryclasses # 是否混淆第三方jar
-dontpreverify # 混淆时是否做预校验
-verbose # 混淆时是否记录日志
-dontpreverify # 不进行预校验,预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # 混淆时所采用的算法
# 保留注解参数
-keepattributes *Annotation*
# 保留了继承自Activity、Application这些类的子类
# 因为这些子类有可能被外部调用
# 比如第一行就保证了所有Activity的子类不要被混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
-keep public class * extends android.support.v4.**
#
# 枚举类不能被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#
# 保留自定义控件(继承自View)不能被混淆
-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(***);
*** get* ();
}
#
# 保留Serializable 序列化的类不被混淆
-keep class * implements java.io.Serializable{
*;
}
# 保留Serializable 序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
# 对于带有回调函数onXXEvent的,不能混淆
-keepclassmembers class * {
void *(**On*Event);
}
#
# 保留native方法的类名和方法名
-keepclasseswithmembernames class * {
native <methods>;
}
#资源不混淆
-keep class **.R$* {*;}
2.针对包下所有的类不参与混淆:
keeppackagenames packagename
还有一种就是keep报下所有的类
-keep class com.test.action.entity.**{*;}
3.如果我们需要把我们的fragment,Activity提供给其他模块使用,需要不混淆fragment,Activity类名或者里面的某一个方法
如果我们只需要keep这个类名:-keep class com.test.fragment.TestFragment{*};
如果需要方法方法,把这个方法放进去即可。注意:方法的参数类型需要指定类的绝对className,不需要指定定义的参数
-keep class com.test.fragment.TestFragment{
public static void getInstance();
public void log(java.lang.String);
public void onEvent(com.test.User);
};
4.EventBus
因为eventBus是通过注册类,在类中进行查找的。有人发现,我们在开发过程中,EventBus发送,代码混淆以后,接收不到,原因是因为onEvent(com.test.Entity)接收类被混淆了,在EventBus代码内部fork不到这个方法以及方法的参数匹配不上
所以要-keep class com.action.fragment.TestFragmrent{
public viod onEvent(com.test.entity.Entity);//参数指定参数classPath,后面不需要指定变量,正常as会关联到这个类的
}
-keep class com.action.fragment.TestActivity{
public viod onEvent(com.test.entity.Entity);//参数指定参数classPath,后面不需要指定变量,正常as会关联到这个类的
}
同样,这个参数也需要keep;
最好把Gson解析的类,或者eventBus,以及需要反射的类放到指定包下,这样只需要keep这个包以及下面所有的类即可。防止丢失
5.aar被主工程二次混淆
在选择混淆压缩的时候,不同的压缩算法会出现不同的混淆效果。R8开启的时候,混淆会被二次混淆,以至于连包名都会被混淆。
5.1包名混淆
关闭R8
5.2 二次混淆
在主工程混淆文件需要keep一下aar或者其他module模块的包名,前提是所有的包都是以报名为前缀的,说检点的,就是你后来创建的包不要在模块报名外。
如果你的报名是:com.org.wiik.action
那么你所有新建的包都是在这个包名下,com.org.wiik.action.entity(package=entity)
这个包你如果已经混淆过了,只要在主工程
-keep class package.**{*;}
-keep class com.org.wiik.action.**{*}
这样就不会对aar进行二次混淆
6.资源被混淆报错:
Andorid NoSuchFieldError: No static field D of type I in class Lx/x/x/R$layout
只需要在混淆文件新增一下即可:
# 保留R下面的资源
-keep class **.R$* {*;}
#不混淆资源类下static的
-keepclassmembers class **.R$* {
public static <fields>;
}