Android 混淆详解

701 阅读5分钟

21_HBD_Mario_1920_1080.jpg

为什么要混淆?

对要进行发布的应用进行混淆,是为避免源代码与资源文件被反编译导致的劳动成果被窃取、安全漏洞被利用等隐患。

混淆的原理是什么?

将代码中的类名、字段、方法名变为无意义的名称,用于保护源代码。同时由于移除无用的类、方法,并使用简短名称对类、字段、方法进行重命名缩小了程序的大小。

ProGuardshrinkoptimizeobfuscatepreverify四个步骤组成,每个步骤都是可选的,需要哪些步骤都可以在脚本中配置。

  • 压缩(Shrink): 侦测并移除代码中无用的类、字段、方法、和特性(Attribute)。

  • 优化(Optimize): 分析和优化字节码。

  • 混淆(Obfuscate): 使用a、b、c、d这样简短而无意义的名称,对类、字段和方法进行重命名。

  • 预检(Preveirfy): 在java平台上对处理后的代码进行预检。

如何在Android里面开启混淆?

在app目录或对应的module目录下的build.gradle的文件中在buildTypes的release里面去做开启与关闭的操作。

buildTypes {
    release {
        shrinkResources true //开启资源压缩
        minifyEnabled true //开启混淆
        zipAlignEnabled true //4k对齐
		// 混淆文件的位置,其中'proguard-android.txt'为sdk默认的混淆配置,
        //'proguard-rules.pro' 是该模块下的混淆配置
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
}

proguard-rules.pro 默认内容如下:

# 在此处添加特定于项目的ProGuard规则。
# 您可以使用build.gradle中的proguardFiles设置控制应用的配置文件集。
# 
# 有关详细信息,请参阅
#   http://developer.android.com/guide/developing/tools/proguard.html

# 如果您的项目使用带有JS的WebView,请取消注释以下内容,并为JavaScript接口指定完全限定的类名
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}

# 取消对此的注释以保留用于调试堆栈跟踪的行号信息。
#-keepattributes SourceFile,LineNumberTable

# 如果保留行号信息,请取消注释以隐藏原始源文件名。
#-renamesourcefileattribute SourceFile


-keep class me.hgj.jetpackmvvm.**{*;}
-keep class com.google.android.material.** {*;}
-keep class androidx.** {*;}
-keep public class * extends androidx.**
-keep interface androidx.** {*;}
-dontwarn com.google.android.material.**
-dontnote com.google.android.material.**
-dontwarn androidx.**

#下面添加第三方依赖库的混淆规则 如XPopup
#xpopup
-dontwarn com.lxj.xpopup.widget.**
-keep class com.lxj.xpopup.widget.**{*;}

Android 混淆的原则

  1. 反射用到的类不混淆。

  2. JNI方法不混淆。

  3. AndroidMainfest中的类不混淆,四大组件和Application的子类和Framework层下所有的类默认不会进行混淆。

  4. Parcelable的子类和Creator静态成员变量不混淆,否则会产生android.os.BadParcelableException异常。

  5. R文件混淆会导致引用错误。

  6. 使用GSON、fastjson等框架时,所写的JSON对象类不混淆,否则无法将JSON解析成对应的对象。

  7. 使用第三方开源库或者引用其他第三方的SDK包时,需要在混淆文件中加入对应的混淆规则。

  8. 有用到WEBView的JS调用也需要保证写的接口方法不混淆。

混淆语法

ProGuard Manual: Usage | Guardsquare

//详细语法
-include {filename}    从给定的文件中读取配置参数   
-basedirectory {directoryname}    指定基础目录为以后相对的档案名称   
-injars {class_path}    指定要处理的应用程序jar,war,ear和目录   
-outjars {class_path}    指定处理完后要输出的jar,war,ear和目录的名称   
-libraryjars {classpath}    指定要处理的应用程序jar,war,ear和目录所需要的程序库文件   
-dontskipnonpubliclibraryclasses    指定不去忽略非公共的库类。   
-dontskipnonpubliclibraryclassmembers    指定不去忽略包可见的库类的成员。  
  
保留选项   
-keep {Modifier} {class_specification}    保护指定的类文件和类的成员   
-keepclassmembers {modifier} {class_specification}    保护指定类的成员,如果此类受到保护他们会保护的更好  
-keepclasseswithmembers {class_specification}    保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在。   
-keepnames {class_specification}    保护指定的类和类的成员的名称(如果他们不会压缩步骤中删除)   
-keepclassmembernames {class_specification}    保护指定的类的成员的名称(如果他们不会压缩步骤中删除)   
-keepclasseswithmembernames {class_specification}    保护指定的类和类的成员的名称,如果所有指定的类成员出席(在压缩步骤之后)   
-printseeds {filename}    列出类和类的成员-keep选项的清单,标准输出到给定的文件   
  
压缩   
-dontshrink    不压缩输入的类文件   
-printusage {filename}   
-whyareyoukeeping {class_specification}       
  
优化   
-dontoptimize    不优化输入的类文件   
-assumenosideeffects {class_specification}    优化时假设指定的方法,没有任何副作用   
-allowaccessmodification    优化时允许访问并修改有修饰符的类和类的成员   
  
混淆   
-dontobfuscate    不混淆输入的类文件   
-printmapping {filename}   
-applymapping {filename}    重用映射增加混淆   
-obfuscationdictionary {filename}    使用给定文件中的关键字作为要混淆方法的名称   
-overloadaggressively    混淆时应用侵入式重载   
-useuniqueclassmembernames    确定统一的混淆类的成员名称来增加混淆   
-flattenpackagehierarchy {package_name}    重新包装所有重命名的包并放在给定的单一包中   
-repackageclass {package_name}    重新包装所有重命名的类文件中放在给定的单一包中   
-dontusemixedcaseclassnames    混淆时不会产生形形色色的类名   
-keepattributes {attribute_name,...}    保护给定的可选属性,例如LineNumberTable, LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and   
  
InnerClasses.   
-renamesourcefileattribute {string}    设置源文件中给定的字符串常量  

#将android.util.Log置为无效代码
-assumenosideeffects class android.util.Log{
   public static boolean isLoggable(java.lang.String,int);
   public static int v(...);
   public static int i(...);
   public static int w(...);
   public static int d(...);
   public static int e(...);
}


关于keep 使用探究可以参考

由浅入深 Android 混淆实战 - 掘金

结束

对于防止应用被反编译以及应用中出现的安全漏洞的封堵,这只是一小步;需要学习的东西还很多。