大家好,跟大家分享一个MAC上混淆打包的经验;
前言:
单挑Android项目,最近即时通讯用到环信,集成sdk的时候 官方有一句
在 ProGuard 文件中加入以下 keep。
1
2
|
-keep class com.hyphenate.** {*;}
-dontwarn com.hyphenate.**
|
即:混淆规则。 自己没写过关于混淆打包的文章,在此补上。
下面了解Android studio环境下 项目混淆打包的操作。
一、打包:
即 将Android项目生成.apk文件,让用户去安装。
1、工具栏 Build->Generate Signed APK..
2、出现这个界面,第一次进入的话编辑框都是空的,然后点击Crete new...按钮
3、这里,填一些相关信息,具体内容就不详说了
4、然后回到第一步,这时候点击按钮Next, Build Type选择release Finish即可,然后在 APK Destination Folder目录下就生成项目的apk包
打包过程如上,over。
二、混淆
做开发的都知道我们可以通过一些工具来反编译一个Apk,得到其中的资源,意图好的也许就是为了参考你项目中优秀的代码。意图不好的也许会反编译Apk来找到你项目中的漏洞,对项目安全做出威胁。
所以现在我们在打包一个项目之前,都要对项目进行混淆处理,让Apk无法被轻易的反编译,提高产品的安全性。
混淆操作需要进行一些配置。
在app目录下的build.gradle文件中修改android{} 区域内代码
1、
1
2
3
4
|
//执行lint检查,有任何的错误或者警告提示,都会终止构建
lintOptions {
abortOnError
false
}
|
2、
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
buildTypes {
debug {
// 显示Log
buildConfigField
"boolean"
,
"LOG_DEBUG"
,
"true"
versionNameSuffix
"-debug"
minifyEnabled
false
zipAlignEnabled
false
shrinkResources
false
signingConfig signingConfigs.debug
}
release {
// 不显示Log
buildConfigField
"boolean"
,
"LOG_DEBUG"
,
"false"
//混淆
minifyEnabled
true
//Zipalign优化
zipAlignEnabled
true
// 移除无用的resource文件
shrinkResources
true
//前一部分代表系统默认的android程序的混淆文件,该文件已经包含了基本的混淆声明,后一个文件是自己的定义混淆文件
proguardFiles getDefaultProguardFile(
'proguard-android.txt'
),
'proguard-rules.pro'
}
}
|
3、修改 proguard
首先是一些固定的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
#指定代码的压缩级别
-optimizationpasses 5
#包明不混合大小写
-dontusemixedcaseclassnames
#不去忽略非公共的库类
-dontskipnonpubliclibraryclasses
#优化 不优化输入的类文件
-dontoptimize
#预校验
-dontpreverify
#混淆时是否记录日志
-verbose
# 混淆时所采用的算法
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#保护注解
-keepattributes *Annotation*
# 保持哪些类不被混淆
-keep public class * extends android.app.Fragment
-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
#如果有引用v4包可以添加下面这行
-keep public class * extends android.support.v4.app.Fragment
#忽略警告
-ignorewarning
##记录生成的日志数据,gradle build时在本项目根目录输出##
#apk 包内所有 class 的内部结构
-dump proguard/class_files.txt
#未混淆的类和成员
-printseeds proguard/seeds.txt
#列出从 apk 中删除的代码
-printusage proguard/unused.txt
#混淆前后的映射
-printmapping proguard/mapping.txt
########记录生成的日志数据,gradle build时 在本项目根目录输出-end######
#如果引用了v4或者v7包
-dontwarn android.support.**
####混淆保护自己项目的部分代码以及引用的第三方jar包library-end####
#保持 native 方法不被混淆
-keepclasseswithmembernames class * {
native <
methods
>;
}
#保持自定义控件类不被混淆
-keepclasseswithmembers class * {
public <
init
>(android.content.Context, android.util.AttributeSet);
}
#保持自定义控件类不被混淆
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-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*(...);
}
#保持 Parcelable 不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#保持 Serializable 不被混淆
-keepnames class * implements java.io.Serializable
#保持 Serializable 不被混淆并且enum 类也不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <
fields
>;
!private <
fields
>;
!private <
methods
>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
#保持枚举 enum 类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclassmembers class * {
public void *ButtonClicked(android.view.View);
}
#不混淆资源类
-keepclassmembers class **.R$* {
public static <
fields
>;
}
#避免混淆泛型 如果混淆报错建议关掉
#-keepattributes Signature
|
然后是根据项目中添加的第三方 额外添加的,一般在第三方的文档中都有
比如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
#gson
#如果用用到Gson解析包的,直接添加下面这几行就能成功混淆,不然会报错。
-keepattributes Signature
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.google.gson.** { *; }
-keep class com.google.gson.stream.** { *; }
#mob
-keep class android.net.http.SslError
-keep class android.webkit.**{*;}
-keep class cn.sharesdk.**{*;}
-keep class com.sina.**{*;}
-keep class m.framework.**{*;}
-keep class **.R$* {*;}
-keep class **.R{*;}
-dontwarn cn.sharesdk.**
-dontwarn **.R$*
#butterknife
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }
-keepclasseswithmembernames class * {
@butterknife.* <
fields
>;
}
-keepclasseswithmembernames class * {
@butterknife.* <
methods
>;
}
######引用的其他Module可以直接在app的这个混淆文件里配置
# 如果使用了Gson之类的工具要使被它解析的JavaBean类即实体类不被混淆。
-keep class com.matrix.app.entity.json.** { *; }
-keep class com.matrix.appsdk.network.model.** { *; }
#####混淆保护自己项目的部分代码以及引用的第三方jar包library#######
#如果在当前的application module或者依赖的library module中使用了第三方的库,并不需要显式添加规则
#-libraryjars xxx
#添加了反而有可能在打包的时候遭遇同一个jar多次被指定的错误,一般只需要添加忽略警告和保持某些class不被混淆的声明。
#以libaray的形式引用了开源项目,如果不想混淆 keep 掉,在引入的module的build.gradle中设置minifyEnabled=false
-keep class com.nineoldandroids.** { *; }
-keep interface com.nineoldandroids.** { *; }
-dontwarn com.nineoldandroids.**
# 下拉刷新
-keep class in.srain.cube.** { *; }
-keep interface in.srain.cube.** { *; }
-dontwarn in.srain.cube.**
# observablescrollview:tab fragment
-keep class com.github.ksoichiro.** { *; }
-keep interface com.github.ksoichiro.** { *; }
-dontwarn com.github.ksoichiro.**
|
至此,执行第一步打包,就可以生成混淆后的Apk了。
三、反编译Apk,验证Apk是否混淆过
这里使用一个工具
dex2jar(源码文件获取) 下载
下载后有这两个文件
1、将打包后的apk文件 手动改变文件类型为.zip ,然后解压缩,会得到一系列文件
找到其中的classes.dex文件(它就是java文件编译再通过dx工具打包而成的)并将它复制到我们下载的dex2jar-2.0文件中去
2、在命令行下定位到dex2jar.bat所在目录,输入
d2j-dex2jar.bat classes.dex
注意第一个命令,需要对应你文件夹中的 d2j-dex2jar.bat文件
在该目录下会生成一个classes_dex2jar.jar的文件,然后打开工具jd-gui文件夹里的jd-gui.exe,
之后用该工具打开之前生成的classes_dex2jar.jar文件,便可以看到源码了,效果如下: 命名变成了a,b,c,d等等说明成功
以上是流程,遇到的问题:
1、proguard 不混淆内部类的方法
保持proguard 不混淆内部类的方法有两种方法:
第一种:
直接-keep整个包下所有的类以及方法不被混淆;
如:-keep class com.AnywayAds.**{*;}
第二种:
-keepnames class com.AnywayAds.Mini$* {
public <fields>;
public <methods>;
}
这种两种方式可以禁止混淆内部类。
我遇到的问题是webview下调用js的时候的一个内部类,被proguard给优化掉了,结果造成无法调用js
补充一下,如果用第二种方法的话,内部类的方法想不被优化掉,必须在代码中调用一下,否则还是会被优化掉。所以还是用第一种会比较好。
2、mac上使用dex2jar遇到的权限问题的解决
调用dex2jar.sh 没有权限 :在dex2jar目录下执行sudo sh d2j-dex2jar.sh classes.dex时报错如下 d2j-dex2jar.sh: 36: d2j-dex2jar.sh: ./d2j_invoke.sh: Permission denied
在dex2jar目录下执行sudo sh d2j-dex2jar.sh classes.dex时报错如下
解决方案 执行命令:
chmod +x d2j-invoke.sh
3、
Android 混淆打包之类名指定
通配符匹配规则
通配符 | 规则 |
---|---|
? | 匹配单个字符 |
* | 匹配类名中的任何部分,但不包含额外的包名 |
** | 匹配类名中的任何部分,并且可以包含额外的包名 |
% | 匹配任何基础类型的类型名 |
* | 匹配任意类型名 ,包含基础类型/非基础类型 |
... | 匹配任意数量、任意类型的参数 |
<init> | 匹配任何构造器 |
<ifield> | 匹配任何字段名 |
<imethod> | 匹配任何方法 |
*(当用在类内部时) | 匹配任何字段和方法 |
$ | 指内部类 |