一、為什么要進(jìn)行代碼混淆大州?
java是一種跨平臺(tái)的续语、解釋型語言、Java源代碼編譯成中間”字節(jié)碼”存儲(chǔ)于class文件中厦画。由于跨平臺(tái)的需要疮茄,Java字節(jié)碼中包括很多源代碼信息,如變量名根暑、方法名力试,并且通過這些名稱來訪問變量和方法,這些符號(hào)帶有許多語義信息排嫌,很容易被反編譯成Java源代碼畸裳。為了防止這種現(xiàn)象,我們可以使用Java混淆器對Java字節(jié)碼進(jìn)行混淆躏率。
二躯畴、什么是代碼混淆?
混淆就是對發(fā)布出去的程序進(jìn)行重新組織和處理薇芝,使用處理后的代碼與處理前代碼完成相同的功能蓬抄,而混淆后的代碼很難被反編譯,即使反編譯成功也很難得出程序的真正語義夯到。被混淆過的程序代碼嚷缭,仍然遵照原來的檔案格式和指令集,執(zhí)行結(jié)果與混淆前一樣,只是混淆器將代碼中的所有變量阅爽、函數(shù)路幸、類的名稱變?yōu)楹喍痰挠⑽淖帜复?hào),在缺乏相應(yīng)的函數(shù)名和程序注釋的情況下付翁,即使被反編譯简肴,也將難以閱讀。同時(shí)混淆是不可逆的百侧,在混淆過程中有一些不影響正常運(yùn)行的信息將永遠(yuǎn)丟失砰识,這些信息的丟失使程序變得更加難以理解。
三佣渴、代碼混淆的作用辫狼?
混淆器的作用不僅僅是保護(hù)代碼,它也有精簡編譯后程序大小的作用辛润。由于以上介紹的縮短變量和函數(shù)名以及丟失部分信息的原因膨处,編譯后jar文件體積大約能減少25%,這對當(dāng)前費(fèi)用較貴的無線網(wǎng)絡(luò)傳輸是有一定意義的砂竖。
四真椿、混淆后apk個(gè)資源文件的比較(見圖)
-
1、包名的比較
混淆前.png
混淆后.png -
2晦溪、變量名
混淆前.png
混淆后.png -
3瀑粥、方法名
混淆前.png
混淆后.png -
4挣跋、字節(jié)碼文件
混淆前.png
混淆后.png
五三圆、代碼混淆怎么做?
-
1避咆、AndroidStudio環(huán)境下代碼混淆涉及到的文件及配置
1. 項(xiàng)目的app/build.gradle
2.代碼混淆工具以及混淆的配置文件(android SDK目錄下)
-
具體代碼混淆操作
首先在app/build.gradle文件中開啟代碼混淆開關(guān)
minifyEnabled true //混淆開關(guān)
默認(rèn)的混淆腳本文件(proguard-android.txt)中設(shè)置了默認(rèn)的保留不混淆條件舟肉,但是大多數(shù)情況下我們?nèi)匀灰O(shè)置我們自己的混淆腳本文件。具體操作如下:
image.png
然后需要在項(xiàng)目生成的混淆腳本中添加過濾混淆的條件
# Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
# Gson
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.stream.** { *; }
# OkHttp3
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
# Okio
-dontwarn com.squareup.**
-dontwarn okio.**
-keep public class org.codehaus.* { *; }
-keep public class java.nio.* { *; }
# Retrofit
-dontwarn retrofit2.**
-keep class retrofit2.** { *; }
-keepattributes Signature
-keepattributes Exceptions
# RxJava RxAndroid
-dontwarn sun.misc.**
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
long producerIndex;
long consumerIndex;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
rx.internal.util.atomic.LinkedQueueNode producerNode;
}
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
rx.internal.util.atomic.LinkedQueueNode consumerNode;
}
#數(shù)據(jù)模型(實(shí)體類)
-keep class com.seekfangle.pad.bean.* {*;}
#第三方架包
-keep class cn.com.** { *;}
-keep class android_serialport_api.** { *;}
配置說明使用到的開源架包(Rxjava查库、Gson等)都能從網(wǎng)上找到混淆配置,直接拷貝到腳本文件中
使用但三方放架包時(shí)如下圖路媚,配置時(shí)最好打開找到其包名采用上述配置文件中的配置方式。
六樊销、混淆為什么要保留類名或方法名整慎?
1、讓C/C++程序可以通過jni使用對應(yīng)的java方法
2围苫、四大組件由于在AndroidManifest.xml里面注冊了裤园,所以需要保留。
3剂府、R文件混淆會(huì)導(dǎo)致引用錯(cuò)誤拧揽。
4、第三方架包有的已經(jīng)經(jīng)過混淆了,再次混淆會(huì)導(dǎo)致找不到類名或者方法名
七淤袜、什么時(shí)候不被混淆痒谴?
一般以下情況都會(huì)不混淆:
1.使用了自定義控件那么要保證它們不參與混淆
2.使用了枚舉要保證枚舉不被混淆
3.對第三方庫中的類不進(jìn)行混淆
4.運(yùn)用了反射的類也不進(jìn)行混淆
5.使用了 Gson 之類的工具要使 JavaBean 類即實(shí)體類不被混淆
6.在引用第三方庫的時(shí)候,一般會(huì)標(biāo)明庫的混淆規(guī)則的铡羡,建議在使用的時(shí)候就把混淆規(guī)則添加上去积蔚,免得到最后才去找
7.有用到 WebView 的 JS 調(diào)用也需要保證寫的接口方法不混淆,原因和第一條一樣
8.Parcelable 的子類和 Creator 靜態(tài)成員變量不混淆烦周,否則會(huì)產(chǎn)生 Android.os.BadParcelableException 異常
八库倘、混淆語法
-
基本規(guī)則
兩個(gè)常用的混淆命令,注意一顆星表示只是保持該包下的類名论矾,而子包下的類名還是會(huì)被混淆教翩;而兩顆星表示把本包和所包含的子包下的類名都保留。-keep class cn.hadcn.test.** -keep class cn.hadcn.test.*
如果既想保持類名贪壳,又想保持里面的內(nèi)容不被混淆饱亿,就執(zhí)行以下方法
-keep class com.example.bean.** { *; }
在此基礎(chǔ)上,我們也可以使用Java的基本規(guī)則來保護(hù)特定類不被混淆闰靴,比如我們可以用extend彪笼,implement等這些Java規(guī)則。如下例子就避免所有繼承Activity的類被混淆
保留我們使用的四大組件蚂且,自定義的Application等等這些類不被混淆
因?yàn)檫@些子類都有可能被外部調(diào)用
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-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 * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
- 2配猫、基本混淆模板
#############################################
#
# 對于一些基本指令的添加
#
############################################## 代碼混淆壓縮比,在0~7之間杏死,默認(rèn)為5泵肄,一般不做修改
-optimizationpasses 5
# 混合時(shí)不使用大小寫混合,混合后的類名為小寫
-dontusemixedcaseclassnames
# 指定不去忽略非公共庫的類
-dontskipnonpubliclibraryclasses
# 這句話能夠使我們的項(xiàng)目混淆后產(chǎn)生映射文件# 包含有類名->混淆后類名的映射關(guān)系
-verbose
# 指定不去忽略非公共庫的類成員
-dontskipnonpubliclibraryclassmembers
# 不做預(yù)校驗(yàn)淑翼,preverify是proguard的四個(gè)步驟之一腐巢,Android不需要preverify,去掉這一步能夠加快混淆速度玄括。
-dontpreverify
# 保留Annotation不混淆
-keepattributes *Annotation*,InnerClasses
# 避免混淆泛型
-keepattributes Signature
# 拋出異常時(shí)保留代碼行號(hào)
-keepattributes SourceFile,LineNumberTable
# 指定混淆是采用的算法冯丙,后面的參數(shù)是一個(gè)過濾器# 這個(gè)過濾器是谷歌推薦的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#############################################
#
# Android開發(fā)中一些需要保留的公共部分
#
#############################################
# 保留我們使用的四大組件遭京,自定義的Application等等這些類不被混淆# 因?yàn)檫@些子類都有可能被外部調(diào)用
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-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 * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
# 保留support下的所有類及其內(nèi)部類
-keep class android.support.** {*;}
# 保留繼承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
# 保留R下面的資源
-keep class **.R$* {*;}
# 保留本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留在Activity中的方法參數(shù)是view的方法胃惜,# 這樣以來我們在layout中寫的onClick就不會(huì)被影響
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
# 保留枚舉類不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保留我們自定義控件(繼承自View)不被混淆
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 保留Parcelable序列化類不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 保留Serializable序列化的類不被混淆
-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();
}
# 對于帶有回調(diào)函數(shù)的onXXEvent、**On*Listener的哪雕,不能被混淆
-keepclassmembers class * {
void *(**On*Event);
void *(**On*Listener);
}
# webView處理船殉,項(xiàng)目中沒有使用到webView忽略即可
-keepclassmembers class fqcn.of.javascript.interface.for.webview {
public *;
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
public boolean *(android.webkit.WebView, java.lang.String);
}
-keepclassmembers class * extends android.webkit.webViewClient {
public void *(android.webkit.webView, jav.lang.String);
}