android下的代码混淆

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/chenli532/article/details/65443456

混淆是打包过程中最重要的流程之一,在没有特殊原因的情况下,所有 app 都应该开启混淆。首先,这里说的的混淆其实是包括了代码压缩、代码混淆以及资源压缩等的优化过程。依靠 ProGuard,混淆流程将主项目以及依赖库中未被使用的类、类成员、方法、属性移除,这有助于规避64K方法数的瓶颈;同时,将类、类成员、方法重命名为无意义的简短名称,增加了逆向工程的难度。而依靠 Gradle 的 Android 插件,我们将移除未被使用的资源,可以有效减小 apk 安装包大小。

###混淆简介


Android中的“混淆”可以分为两部分,一部分是 Java 代码的优化与混淆,依靠 proguard 混淆器来实现;另一部分是资源压缩,将移除项目及依赖的库中未被使用的资源(资源压缩严格意义上跟混淆没啥关系,但一般我们都会放一起讲)。

1. 代码压缩
代码混淆是包含了代码压缩、优化、混淆等一系列行为的过程。混淆过程会有如下几个功能:

压缩。移除无效的类、类成员、方法、属性等;

优化。分析和优化方法的二进制代码;根据proguard-android-optimize.txt中的描述,优化可能会造成一些潜在风险,不能保证在所有版本的Dalvik上都正常运行。

混淆。把类名、属性名、方法名替换为简短且无意义的名称;

预校验。添加预校验信息。这个预校验是作用在Java平台上的,Android平台上不需要这项功能,去掉之后还可以加快混淆速度。

这四个流程默认开启。

在 Android 项目中我们可以选择将“优化”和“预校验”关闭,对应命令是-dontoptimize、-dontpreverify(当然,默认的 proguard-android.txt 文件已包含这两条混淆命令,不需要开发者额外配置)。


2. 资源压缩
资源压缩将移除项目及依赖的库中未被使用的资源,这在减少 apk 包体积上会有不错的效果,一般建议开启。具体做法是在 build.grade 文件中,将 shrinkResources 属性设置为 true。需要注意的是,只有在用minifyEnabled true开启了代码压缩后,资源压缩才会生效。

资源压缩包含了“合并资源”和“移除资源”两个流程。

“合并资源”流程中,名称相同的资源被视为重复资源会被合并。需要注意的是,这一流程不受shrinkResources属性控制,也无法被禁止, gradle 必然会做这项工作,因为假如不同项目中存在相同名称的资源将导致错误。gradle 在四处地方寻找重复资源:

src/main/res/ 路径

不同的构建类型(debug、release等等)

不同的构建渠道

项目依赖的第三方库

合并资源时按照如下优先级顺序:

依赖 -> main -> 渠道 -> 构建类型
举个例子,假如重复资源同时存在于main文件夹和不同渠道中,gradle 会选择保留渠道中的资源。

同时,如果重复资源在同一层次出现,比如src/main/res/ 和 src/main/res/,则 gradle 无法完成资源合并,这时会报资源合并错误。

“移除资源”流程则见名知意,需要注意的是,类似代码,混淆资源移除也可以定义哪些资源需要被保留。



eclipse下的混淆:

在project.properties中将11行改成proguard.config=proguard-project.txt将前面的#去掉,复制sdk/tools/proguard/下的proguard-android.txt到工程的根目录下。

或者使用proguard-project.txt这个包也可以。
proguard.config=proguard-project.txt

我之前在设置混淆的时候出现了警告信息,设置忽略警告。

-dontwarn com.unionpay.**
设置某一个包下的所有class文件不混淆
-keep class com.unionpay.** {*;}

一般一起使用
	-dontwarn com.unionpay.**
	-keep class com.unionpay.** {*;}

android studio下的混淆

####1. 混淆配置
一般情况下,app module 的 build.gradle 文件默认会有如下结构:

android {
	    buildTypes {
	        release {
	            minifyEnabled false
	            proguardFiles getDefaultProguardFile('proguard-android.txt'),
								 'proguard-rules.pro'
	        }
	    }
	}

因为开启混淆会使编译时间变长,所以debug模式下不应该开启。

开启混淆,我们需要做的是:

1. 将release下minifyEnabled的值改为true,打开混淆
2. 加上shrinkResources true,打开资源压缩。

修改后文件内容如下:

	android {
	    buildTypes {
	        release {
	            minifyEnabled true
	            shrinkResources true
	            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
	        }
	    }
	}

#### 2. 自定义混淆规则

###### 指定压缩级别

	-optimizationpasses 5


##### 不跳过非公共的库的类成员

	-dontskipnonpubliclibraryclassmembers

##### 混淆时采用的算法

	-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
##### 把混淆类中的方法名也混淆了
	-useuniqueclassmembernames
##### 优化时允许访问并修改有修饰符的类和类的成员 
	-allowaccessmodification
##### 保留行号
	-keepattributes SourceFile,LineNumberTable
##### 保持所有实现 Serializable 接口的类成员
	-keepclassmembers class * implements java.io.Serializable {
	    static final long serialVersionUID;
	    private static final java.io.ObjectStreamField[] serialPersistentFields;
	    private void writeObject(java.io.ObjectOutputStream);
	    private void readObject(java.io.ObjectInputStream);
	    java.lang.Object writeReplace();
	    java.lang.Object readResolve();
	}

##### Fragment不需要在AndroidManifest.xml中注册,需要额外保护下
	-keep public class * extends android.support.v4.app.Fragment
	-keep public class * extends android.app.Fragment

##### 保持测试相关的代码

	-dontnote junit.framework.**
	-dontnote junit.runner.**
	-dontwarn android.test.**
	-dontwarn android.support.test.**
	-dontwarn org.junit.**


常用的自定义混淆规则


不混淆某个类

	-keep public class name.xxxx.example.Test { *; }

不混淆某个包所有的类

	-keep class name.huihui.test.** { *; }

不混淆某个类的子类

	-keep public class * extends name.xxxx.example.Test { *; }

不混淆所有类名中包含了“model”的类及其成员

	-keep public class **.*model*.** {*;}

不混淆某个接口的实现
	-keep class * implements name.xxxx.example.TestInterface { *; }



不混淆某个类的构造方法

-keepclassmembers class name.xxxx.example.Test { 
 	public <init>(); 
}


不混淆某个类的特定的方法

-keepclassmembers class name.xxxx.example.Test { 
 	public void test(java.lang.String); 
}


#保护注解

	-keepattributes *Annotation*

#保持所有拥有本地方法的类名及本地方法名
-keepclasseswithmembernames class * {
    native <methods>;
}

 # 保持自定义View的get和set相关方法

-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}

##### 检查混淆结果

混淆过的包必须进行检查,避免因混淆引入的bug。
一方面,需要从代码层面检查。使用上文的配置进行混淆打包后在 <module-name>/build/outputs/mapping/release/ 目录下会输出以下文件:

dump.txt
描述APK文件中所有类的内部结构

mapping.txt
提供混淆前后类、方法、类成员等的对照表

seeds.txt
列出没有被混淆的类和成员

usage.txt
列出被移除的代码:

我们可以根据 seeds.txt 文件检查未被混淆的类和成员中是否已包含所有期望保留的,再根据 usage.txt 文件查看是否有被误移除的代码。

另一方面,需要从测试方面检查。将混淆过的包进行全方面测试,检查是否有 bug 产生。





展开阅读全文

没有更多推荐了,返回首页