as代码混淆

来源:互联网 发布:webservice json 编辑:程序博客网 时间:2024/06/15 17:47
混淆流程将主项目以及依赖库中未被使用的类、类成员、方法、属性移除,这有助于规避64K方法数的瓶颈;同时,将类、类成员、方法重命名为无意义的简短名称,增加了逆向工程的难度。而依靠 Gradle的Android 插件,我们将移除未被使用的资源,可以有效减小 apk 安装包大小。

在app module下默认生成了项目的自定义混淆规则文件proguard-rules.pro。

先上个干货,一般情况的混淆配置(含注解):

混淆

1.app/build.gradle/buildTypes把minifyEnabled从false改成ture来启动Proguard混淆功能(AS)。

  1. buildTypes {  
  2.     release {  
  3.         minifyEnabled true  
  4.         proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
  5.     }  


[java]view plain copy
  1. #忽略警告,避免打包时某些警告出现  
  2. -ignorewarnings  
  3. #指定压缩级别  
  4. -optimizationpasses 5  
  5. #包名不混合大小写  
  6. -dontusemixedcaseclassnames  
  7. #不跳过非公共的库的类  
  8. -dontskipnonpubliclibraryclasses  
  9. -dontskipnonpubliclibraryclassmembers  
  10. #不优化输入的类文件  
  11. -dontoptimize  
  12. #关闭预校验  
  13. -dontpreverify  
  14. #混淆时记录日志  
  15. -verbose  
  16. -printmapping proguardMapping.txt  
  17. #混淆时采用的算法  
  18. -optimizations !code/simplification/cast,!field/*,!class/merging/*  
  19. #保护注解  
  20. -keepattributes *Annotation*,InnerClasses  
  21. -keepattributes Signature  
  22. #保留行号  
  23. -keepattributes SourceFile,LineNumberTable  
  24. #keep相关注解  
  25. -keep class android.support.annotation.Keep  
  26. -keep @android.support.annotation.Keep class * {*;}  
  27. -keepclasseswithmembers class * {  
  28.     @android.support.annotation.Keep <methods>;  
  29. }  
  30. -keepclasseswithmembers class * {  
  31.     @android.support.annotation.Keep <fields>;  
  32. }  
  33. -keepclasseswithmembers class * {  
  34.     @android.support.annotation.Keep <init>(...);  
  35. }  
  36. -keep public class * extends android.app.Activity  
  37. -keep public class * extends android.app.Appliction  
  38. -keep public class * extends android.app.Service  
  39. -keep public class * extends android.content.BroadcastReceiver  
  40. -keep public class * extends android.content.ContentProvider  
  41. -keep public class * extends android.app.backup.BackupAgentHelper  
  42. -keep public class * extends android.preference.Preference  
  43. -keep public class * extends android.view.View  
  44. -keep public class com.android.vending.licensing.ILicensingService  
  45. -keep class android.support.** {*;}  
  46. #保持所有拥有本地方法的类名及本地方法名  
  47. -keepclasseswithmembernames class * {  
  48.     native <methods>;  
  49. }  
  50. #保持Activity中View及其子类入参的方法  
  51. -keepclassmembers class * extends android.app.Activity{  
  52.     public void *(android.view.View);  
  53. }  
  54. #枚举  
  55. -keepclassmembers enum * {  
  56.     public static **[] values();  
  57.     public static ** valueOf(java.lang.String);  
  58. }  
  59. #保持自定义View的get和set相关方法  
  60. -keep public class * extends android.view.View{  
  61.     *** get*();  
  62.     void set*(***);  
  63.     public <init>(android.content.Context);  
  64.     public <init>(android.content.Context, android.util.AttributeSet);  
  65.     public <init>(android.content.Context, android.util.AttributeSet, int);  
  66. }  
  67. -keepclasseswithmembers class * {  
  68.     public <init>(android.content.Context, android.util.AttributeSet);  
  69.     public <init>(android.content.Context, android.util.AttributeSet, int);  
  70. }  
  71. #Parcelable  
  72. -keep class * implements android.os.Parcelable {  
  73.   public static final android.os.Parcelable$Creator *;  
  74. }  
  75. #Fragment不需要在AndroidManifest.xml中注册,需要额外保护下  
  76. -keep public class * extends android.support.v4.app.Fragment  
  77. -keep public class * extends android.app.Fragment  
  78. #保持所有实现Serializable接口的类成员  
  79. -keepclassmembers class * implements java.io.Serializable {  
  80.     static final long serialVersionUID;  
  81.     private static final java.io.ObjectStreamField[] serialPersistentFields;  
  82.     private void writeObject(java.io.ObjectOutputStream);  
  83.     private void readObject(java.io.ObjectInputStream);  
  84.     java.lang.Object writeReplace();  
  85.     java.lang.Object readResolve();  
  86. }  
  87. -keep class **.R$* {  
  88.  *;  
  89. }  
  90. -keepclassmembers class * {  
  91.     void *(**On*Event);  
  92. }  
  93. #webview  
  94. -keepclassmembers class fqcn.of.javascript.interface.for.webview {  
  95.    public *;  
  96. }  
  97. -keepclassmembers class * extends android.webkit.webViewClient {  
  98.     public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);  
  99.     public boolean *(android.webkit.WebView, java.lang.String);  
  100. }  
  101. -keepclassmembers class * extends android.webkit.webViewClient {  
  102.     public void *(android.webkit.webView, jav.lang.String);  
  103. }  
混淆的规则有哪些呢:

1、保持相关元素不参与混淆的规则相关的几种命令:

[java]view plain copy
  1. -keep:防止类和成员被移除或者被重命名  
  2. -keepnames:防止类和成员被重命名  
  3. -keepclassmembers:防止成员被移除或者被重命名  
  4. -keepnames:防止成员被重命名  
  5. -keepclasseswithmembers:防止拥有该成员的类和成员被移除或者被重命名  
  6. -keepclasseswithmembernames:防止拥有该成员的类和成员被重命名  
2、保持元素不参与混淆的规则,形如:
[java]view plain copy
  1. [保持命令] [类] {  
  2.     [成员]   
  3. }  
“类”代表类相关的限定条件,它将最终定位到某些符合该限定条件的类。它的内容可以使用:
具体的类:
访问修饰符(public、protected、private);
通配符*,匹配任意长度字符,但不含包名分隔符(.);
通配符**,匹配任意长度字符,并且包含包名分隔符(.);
extends,即可以指定类的基类;
implement,匹配实现了某接口的类;
$,内部类;
“成员”代表类成员相关的限定条件,它将最终定位到某些符合该限定条件的类成员。它的内容可以使用:
匹配所有构造器
匹配所有域
匹配所有方法
通配符*,匹配任意长度字符,但不含包名分隔符(.)
通配符**,匹配任意长度字符,并且包含包名分隔符(.)
通配符***,匹配任意参数类型
…,匹配任意长度的任意类型参数。

比如void test(…)就能匹配任意void test(String a)或者是void test(int a, String b) 这些方法。
访问修饰符(public、protected、private)
举个例子,假如需要将name.huihui.test包下所有继承Activity的public类及其构造函数都保持住,可以这样写:

[java]view plain copy
  1. -keep public class name.huihui.test.** extends Android.app.Activity {  
  2.     <init>  
  3. }  


常用的自定义混淆规则如下:
[java]view plain copy
  1. #不混淆某个类  
  2. -keep public class name.huihui.example.Test { *; }  
  3. #不混淆某个包所有的类  
  4. -keep class name.huihui.test.** { *; }  
  5. #不混淆某个类的子类  
  6. -keep public class * extends name.huihui.example.Test { *; }  
  7. #不混淆所有类名中包含了“model”的类及其成员  
  8. -keep public class **.*model*.** {*;}  
  9. #不混淆某个接口的实现  
  10. -keep class * implements name.huihui.example.TestInterface { *; }  
  11. #不混淆某个类的构造方法  
  12. -keepclassmembers class name.huihui.example.Test {   
  13.   public <init>();   
  14. }  
  15. #不混淆某个类的特定的方法  
  16. -keepclassmembers class name.huihui.example.Test {   
  17.   public void test(java.lang.String);   
  18. }  


其他几种混淆:

1、实体类由于涉及到与服务端的交互,各种gson的交互如此等等,是要保留的。将你项目中实体类都拎出来,用以下语法进行保留。

[java]view plain copy
  1. -keep class 你的实体类所在的包.** { *; }  
如我的项目下类User的完整路径为:com.demo.login.bean.User, 那我的混淆如下
[java]view plain copy
  1. -keep class com.demo.login.bean.** { *; }  
2、第三方包。打开你的build.gradle文件,查看你用了哪些第三方的包。
[java]view plain copy
  1. dependencies {  
  2. }  
去他们的官网把已经写好的混淆copy下来。
3、与js互调的类。
[java]view plain copy
  1. -keep class 你的类所在的包.** { *; }  
如果是内部类的话,你可以这样
[java]view plain copy
  1. -keepclasseswithmembers class 你的类所在的包.父类$子类 { <methods>; }  
例如
[java]view plain copy
  1. -keepclasseswithmembers class com.demo.login.bean.ui.MainActivity$JSInterface {   
  2.       <methods>;   
  3. }  
4、与反射有关的类。
假如项目命名规范要求实体类都要放在model包下的话,可以添加类似这样的代码把所有实体类都保持住:
[java]view plain copy
  1. -keep public class **.*Model*.** {*;}  
类的话直接这样
[java]view plain copy
  1. -keep class 你的类所在的包.** { *; }  

最后将build.gradle中minifyEnabled设置为true,shrinkResources设置为true,打开资源压缩。
注意,只有在用minifyEnabled开启了,资源压缩才会生效。

[java]view plain copy
  1. release {         
  2.     minifyEnabled true  
  3.     shrinkResources true  
  4.     proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
  5. }  
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
这行代码定义了混淆规则由两部分构成:位于SDK的tools/proguard/文件夹中的proguard-android.txt的内容以及默认放置于模块根目录的proguard-rules.pro的内容。前者是SDK提供的默认混淆文件(内容见附录1),后者是开发者自定义混淆规则的地方。

检查混淆结果:混淆过的包必须进行检查,避免因混淆引入的bug。
使用上文的配置进行混淆打包后在 <module-name>/build/outputs/mapping/release/ 目录下会输出以下文件:
dump.txt:描述APK文件中所有类的内部结构;
mapping.txt:提供混淆前后类、方法、类成员等的对照表;
seeds.txt:列出没有被混淆的类和成员;
usage.txt:列出被移除的代码;
我们可以根据seeds.txt文件检查未被混淆的类和成员中是否已包含所有期望保留的,再根据usage.txt文件查看是否有被误移除的代码。


解出混淆
混淆后,对追踪线上crash也造成了阻碍。我们拿到crash的堆栈信息后会发现很难定位,这时需要将混淆反解。
在<sdk-root>/tools/proguard/bin路径下有附带反解工具(Window系统为proguardgui.bat,Mac或Linux系统为proguardgui.sh)。
这里以Window平台为例。双击运行proguardgui.bat后,可以看到左侧的一行菜单。点击ReTrace,选择该混淆包对应的mapping文件(混淆后在<module-name>/build/outputs/mapping/release/路径下会生成mapping.txt文件,它的作用是提供混淆前后类、方法、类成员等的对照表),再将crash的stack trace黏贴进输入框中,点击右下角的ReTrace,混淆后的堆栈信息就显示出来了。
另一种方式是利用该路径下的retrace工具通过命令行进行反解,命令是

[java]view plain copy
  1. retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]  
例如:
[java]view plain copy
  1. retrace.bat -verbose mapping.txt obfuscated_trace.txt 
原创粉丝点击