关于混淆
代码混淆(Obfuscated code)亦称花指令,是将计算机程序的代码,转换成一种功能上等价,但是难于阅读和理解的形式的行为。代码混淆可以用于程序源代码,也可以用于程序编译而成的中间代码。执行代码混淆的程序被称作代码混淆器。目前已经存在许多种功能各异的代码混淆器。
众所周知,Java虽然是编译型的语言,但是由于Java编译后的字节码的抽象级别较高,因此它们较容易被反编译。为了防止我们的劳动成果被人窃取,我们通常会将Java程序混淆后打包,增大别人反编译时的难度。
当然,并不是混淆后,别人就无法反编译我们的程序了,否则这个混淆就可以改为加密了。代码混淆并不能真正阻止反向工程,只能增大其难度。因此,对于对安全性要求很高的场合,仅仅使用代码混淆并不能保证源代码的安全。混淆是将代码中的各种元素,如变量,函数,类的名字改写成无意义的名字,然后进行打包,加大别人反编译我们的程序后的理解难度。
另外,因为混淆过程中会精简被混淆的类的类、变量、函数的名字,丢弃无用类和资源,因此混淆也会在一定程度上压缩编译后的文件大小。
混淆也会给我们自己带来一些问题。首先是时间成本上的增加,虽然混淆很简单,短则一两个小时,多则一两天就可以搞定,但是这个时间成本依旧存在。更重要的是,混淆会给我们的调试带来很大的困难,精简后的类、函数、变量名,给我们定位出错带来了一定的难度。对于支持反射的Java,代码混淆有可能与反射发生冲突,因此在与反射相关的类需要避免混淆。
Android的代码混淆默认使用ProGuard工具,在Android Developer上有相关介绍。
混淆配置步骤
以Android Studio为例。
- 首先,我们需要在Module中的build.gradle中进行混淆配置。通常,我们在混淆时,只对release版本进行混淆,debug版本混淆很明显除了给自己调试增加难度外,没任何用。配置示例如下:
apply plugin: 'com.android.application'
android {
...
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
minifyEnabled true
即表示开启混淆。下一行proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
是指定多个混淆配置文件,getDefaultProguardFile('proguard-android.txt')
表示使用默认配置文件proguard-android.txt
,后面的proguard-rules.pro
即为我们自己编写的混淆配置文件。
- 在build.gradle指定的文件中,按照ProGuard语法编写混淆配置。
- 签名打包。
ProGuard语法
#指定代码的压缩级别0-7
-optimizationpasses 5
#是否使用大小写混合
-dontusemixedcaseclassnames
#是否混淆第三方jar
-dontskipnonpubliclibraryclasses
#混淆时是否做预校验
-dontpreverify
#混淆时是否记录日志
-verbose
# 混淆时所采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#从给定的文件中读取配置参数
-include {filename}
#指定基础目录为以后相对的档案名称
-basedirectory {directoryname}
#指定要处理的应用程序jar,war,ear和目录
-injars {class_path}
#指定处理完后要输出的jar,war,ear和目录的名称
-outjars {class_path}
#指定要处理的应用程序jar,war,ear和目录所需要的程序库文件
-libraryjars {classpath}
#指定不去忽略非公共的库类。
-dontskipnonpubliclibraryclasses
#指定不去忽略包可见的库类的成员。
-dontskipnonpubliclibraryclassmembers
## 保留选项
#保护指定的类文件和类的成员
-keep {Modifier} {class_specification}
#保护指定类的成员,如果此类受到保护他们会保护的更好
-keepclassmembers {modifier} {class_specification}
#保护指定的类和类的成员,但条件是所有指定的类和类成员是要存在。
-keepclasseswithmembers {class_specification}
#保护指定的类和类的成员的名称(如果他们不会压缩步骤中删除)
-keepnames {class_specification}
#保护指定的类的成员的名称(如果他们不会压缩步骤中删除)
-keepclassmembernames {class_specification}
#保护指定的类和类的成员的名称,如果所有指定的类成员出席(在压缩步骤之后)
-keepclasseswithmembernames {class_specification}
#列出类和类的成员-keep选项的清单,标准输出到给定的文件
-printseeds {filename}
## 压缩
#不压缩输入的类文件
-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
#保护给定的可选属性,例如LineNumberTable,LocalVariableTable, SourceFile, Deprecated, Synthetic, Signature, and InnerClasses.
-keepattributes {attribute_name,...}
#设置源文件中给定的字符串常量
-renamesourcefileattribute {string}
混淆示例
# 指定代码的压缩级别
-optimizationpasses 5
-dontusemixedcaseclassnames
# 是否混淆第三方jar
-dontskipnonpubliclibraryclasses
-dontpreverify
-keepattributes SourceFile,LineNumberTable
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-libraryjars libs/httpmime-4.1.3.jar
-libraryjars libs/libammsdk.jar
-libraryjars libs/fastjson-1.1.34.android.jar
-libraryjars libs/commons-lang.jar
-libraryjars libs/weibosdkcore.jar
# webview + js
# keep 使用 webview 的类
-keepclassmembers class com.goldnet.mobile.activity.InfoDetailActivity {
public *;
}
# keep 使用 webview 的类的所有的内部类
-keepclassmembers class com.goldnet.mobile.activity.InfoDetailActivity$*{
*;
}
# 保持哪些类不被混淆
-keep class android.** {*; }
-keep public class * extends android.view
-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.pm
-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
-keepattributes *Annotation*
# 保持 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);
}
-keepclasseswithmembers class * {
void onClick*(...);
}
-keepclasseswithmembers class * {
*** *Callback(...);
}
# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
# 保持枚举 enum 类不被混淆
-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 *;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
# http client
-keep class org.apache.http.** {*; }
# keep 泛型
-keepattributes Signature
# 新浪微博
-keep class com.sina.**{*;}
# volley
-dontwarn com.android.volley.jar.**
-keep class com.android.volley.**{*;}
# actionbarsherlock
-dontwarn com.actionbarsherlock.**
-keep class com.actionbarsherlock.**{*;}
-dontwarn com.cairh.app.sjkh.**
-keep class com.cairh.app.sjkh.**{*;}
混淆注意事项
根据代码混淆的原理(最主要的就是反射需要用到包名和类名方法名,而混淆会修改类名为其它无意义名字),Android代码在使用混淆使需要注意:
- Android系统组件应避免混淆。
- 被Jni调用的Java类要避免混淆。
- 所有的native方法不能被混淆
- 自定义View不应该被混淆。
- 枚举类不应该被混淆。
- 注解不应该被混淆
- 序列化的类(Parcelable)要避免混淆
- 利用GSON等解析工具解析Json的Bean类,不应该被混淆。
- 数据库驱动不应该被混淆。
- aidl文件不能被混淆
- Android建议不要混淆某些类,比如BackupAgent、ILicensingService等
- 在使用第三方工程的时候,一般会有说明混淆时候要注意哪些要避免混淆,按照其说明加入混淆配置中。
默认混淆
对于以上提到的注意事项,有些使用过混淆的朋友可能会觉得奇怪,我没有避免混淆Android系统组件,比如Activity,Service等等,为什么也没问题呢?这是因为在Android的默认配置中,已经做了相关配置,默认配置如下:
# This is a configuration file for ProGuard.
# http://proguard.sourceforge.net/index.html#manual/usage.html
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
# Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize and preverify steps (and performs some
# of these optimizations on its own).
-dontoptimize
-dontpreverify
# If you want to enable optimization, you should include the
# following:
# -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/*
# -optimizationpasses 5
# -allowaccessmodification
#
# Note that you cannot just include these flags in your own
# configuration file; if you are including this file, optimization
# will be turned off. You'll need to either edit this file, or
# duplicate the contents of this file and remove the include of this
# file from your project's proguard.config path property.
-keepattributes *Annotation*
-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.BackupAgent
-keep public class * extends android.preference.Preference
-keep public class * extends android.support.v4.app.Fragment
-keep public class * extends android.app.Fragment
-keep public class com.android.vending.licensing.ILicensingService
# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
-keepclasseswithmembernames class * {
native <methods>;
}
-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*(...);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version. We know about them, and they are safe.
-dontwarn android.support.**
欢迎转载,转载请保留文章出处。湖广午王的博客[http://blog.csdn.net/junzia/article/details/52879823]