Android混淆

xiaoxiao2021-02-27  236

Proguard特性

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

混淆配置

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文件中保留了。

转载请注明原文地址: https://www.6miu.com/read-11653.html

最新回复(0)