Android混淆

来源:互联网 发布:迅龙数据恢复免费版 编辑:程序博客网 时间:2024/05/16 16:08

Proguard特性

  1. 压缩:Java源代码通常被编译为字节码,虽然字节码比源代码更简洁,但它本身仍然会包含很多无用的代码。Proguard的压缩功能通过分析字节码,能够检测并移除没有使用的类,字段,方法和属性;
  2. 优化:优化Java字节码,同时移除没有使用到的指令;
  3. 混淆:使用无意义的简短字母组合对类名、字段名、方法名进行重命名;
  4. 预检验:对上述处理后的代码进行预检验;

混淆配置

buildTypes {        release {            minifyEnabled true  //true表示使能Proguard            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'        }    }

混淆文件的规则分类

  • 公共的混淆规则:每个APP都通用的,主要是对Proguard的基本配置,以及Android SDK中API设置的规则,例如Activity、Parcelable等;
  • App特有的混淆规则:根据APP自身的特点进行设置,例如某些类会被反射调用,如果被混淆,那么反射就找不到类了;
  • 第三方函数库或者SDK的混淆规则:如果APP引入了第三方开源函数库或者SDK,那么需要查看这些函数库或者SDK的使用说明,将需要去混淆的地方加上去;

混淆文件编写

#代码迭代优化的次数,取值范围0 - 7,默认5-optimizationpasses 5#混淆时不使用大小写混合的方式,这样混淆后都是小写字母的组合-dontusemixedcaseclassnames#混淆时不做欲校验,欲校验是Proguard四大功能之一,在Android中一般不需要欲校验,这样可以加快混淆的速度-dontpreverify#混淆时记录日志,同时会生成映射文件,Android Studio中,生成的默认映射文件是 'build/outputs/mapping/release/mapping.txt'-verbose#生成指定的映射文件的路径和名称-printmapping build/outputs/mapping/release/mymapping.txt#混淆时所采用的算法,参数是Google官方推荐的过滤器算法-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*#如果项目中用到了注解,需要保留注解属性-keepattributes *Annotation*#不混淆泛型-keepattributes Signature#保留代码行号,这在混淆后代码运行中抛出异常信息时,有利于定位出问题的代码-keepattributes SourceFile,LineNumberTable#保持Android SDK相关API类不被混淆-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 com.android.vending.licensing.ILicensingService#保留R类-keep class **.R$*{    *;}#保留native方法不被混淆-keepclasseswithmembernames class * {    native<methods>;}#保持自定义控件类不被混淆-keepclasseswithmembers class * {    public <init>(android.content.Context,android.util.AttributeSet);}-keepclasseswithmembers class * {    public <init>(android.content.Context,android.util.AttributeSet,int);}#保持Activity中参数是View类型的参数,保证在Layout XML文件配置的 onClick 属性的值能够正常调用到-keepclassmembers class * extends android.app.Activity{    public void *(android.view.View);}#保持枚举类不被混淆-keepclassmembers enum * {    public static ** [] values();    public static ** valueOf(java.lang.String);}#保持Parcelable不被混淆-keep class * implements android.os.Parcelable{    public static final android.os.Parcelable$Creator *;}#保持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();}#保持自定义控件不被混淆-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*();}#引入各个开源库需要增加的混淆

针对App的量身定制

假设我们创建一个项目,包名和结构名如下:

这里写图片描述

1、保留实体类和成员不被混淆
对于实体类,要保留它们的set和get方法,对于boolean型get方法,有人喜欢命名为isXXX方式,所以不要遗漏。一种好的做法是把所有实体都放在一个包下管理,这样只写一次混淆就够了,避免在别的包中新增实体而忘记保留,代码在混淆后因为找不到实体类而崩溃。

-keep public class heyha.nj.com.heyha.entity.** {    public void set*(***);    public *** get*();    public *** is*();}

2、内嵌类
内嵌类经常被混淆,结果在调用的时候为空就崩溃了。最好的解决办法就是把这个内嵌类拿出来,单独成类。如果一定要内置,那这个类就必须在混淆时保留,例如保留heyha.nj.com.heyha.activity包下MainActivity的所有内嵌类,代码如下:$是用于分割内嵌类与其母体的标识。

-keep class com.heyha.nj.heyha.activity.MainActivity$* {    *;}

3、对WebView的处理
如果用到了webview的复杂操作,需要添加如下代码:

-keepclassmembers class * extends android.webkit.webViewClient {    public void *(android.webkit.WebView,java.lang.String,android.graphics.Bitmap);    public boolean *(android.webkit.WebView,java.lang.String);}-keepclassmembers class * extends android.webkit.webViewClient {    public void *(android.webkit.webView,java.lang.String);}

4、对JavaScript的处理
App与HTML5页面的JavaScript进行交互,如下所示:

class JSInteface1 {    @JavascriptInterface    public void callAndroidMethod(int a, float b, String c, boolean d){        if(d){            String strMessage = "-" + (a + 1) + "-" + (b + 1) + "-" + c + "-" + d;            new AlertDialog.Builder(MainActivity.this).setTitle("title")            .setMessage(strMessage).show();        }    }}

JSInteface1是MainActivity的子类,所以保留指令写法如下,而且需要对所有使用的地方设置保留指令

-keepclassmembers class com.heyha.nj.heyha.activity.MainActivity$JSInteface1 {    <methods>;}

5、处理反射
在混淆过程中,无论是Class.forName(“SomeClass”),还是SomeClass.class,SomeClass这个类的名称都会被混淆,因此,在混淆文件中,需要保留这个类。此外,还有如下方法:

SomeClass.class.getField("someField")SomeClass.class.getDeclaredField("someField")SomeClass.class.getMethod("someMethod",new Class[]{})SomeClass.class.getMethod("someMethod",new Class[]{A.class})SomeClass.class.getMethod("someMethod",new Class[]{A.class,B.class})SomeClass.class.getDeclaredMethod("someMethod",new Class[]{})SomeClass.class.getDeclaredMethod("someMethod",new Class[]{A.class})SomeClass.class.getDeclaredMethod("someMethod",new Class[]{A.class,B.class})AtomicIntegerFieldUpdater.newUpdater(SomeClass.class,"someField")AtomicLongFieldUpdater.newUpdater(SomeClass.class,"someField")AtomicReferenceFieldUpdater.newUpdater(SomeClass.class,SomeType.class,"someField")

6、自定义view的解决方案
凡是在layout目录中的xml布局文件中配置的自定义View,都不能进行混淆。为此要遍历layout下所有的xml布局文件,找到那些自定义view,然后确认其是否在proguard文件中保留了。

原创粉丝点击