溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android混淆

發(fā)布時間:2020-07-27 08:00:06 來源:網(wǎng)絡 閱讀:620 作者:lzwxx 欄目:移動開發(fā)

一、為什么要混淆

為了避免apk在發(fā)布后被用戶通過反編譯拿到源代碼和資源文件,然后修改資源和代碼之后就變成一個新的apk。而經過混淆后的APK,即使被反編譯,也難以閱讀,注意混淆不是讓apk不能閱讀,而是加大閱讀的難度,為了避免勞動成果被竊取,也避免出現(xiàn)安全漏洞和隱患,所以在apk發(fā)布之前一定要進行混淆。

二、混淆的原理

Java是一種跨平臺、解釋型語言,Java源代碼編譯成的class文件中有大量包含語義的變量名、方法名的信息,很容易被反編譯為Java源代碼。為了防止這種現(xiàn)象,我們可以對Java字節(jié)碼進行混淆。混淆不僅能將代碼中的類名、字段、方法名變?yōu)闊o意義的名稱,保護代碼,也由于移除無用的類、方法,并使用簡短名稱對類、字段、方法進行重命名縮小了程序的大小。

ProGuard由shrink、optimize、obfuscate和preverify四個步驟組成,每個步驟都是可選的,需要哪些步驟都可以在腳本中配置。參見ProGuard官方介紹。
  壓縮(Shrink):默認開啟,偵測并移除代碼中無用的類、字段、方法和特性,減少應用體積,并且會在優(yōu)化動作執(zhí)行之后再次執(zhí)行(因為優(yōu)化后可能會再次暴露一些未使用的類和成員)。
    -dontshrink 關閉混淆
  優(yōu)化(Optimize):默認開啟,分析和優(yōu)化字節(jié)碼,讓應用運行的更快。
    -dontoptimize 關閉優(yōu)化,默認混淆配置文件開始
    -optimizationpasses n 表示proguard對代碼進行迭代優(yōu)化的次數(shù),Android一般為5
  混淆(Obfuscate):默認開啟,使用a、b、c、d這樣簡短而無意義的名稱,對類、字段和方法進行重命名,增大反編譯難度。
    -dontobfuscate 關閉混淆
上面三個步驟使代碼大小更小、更高效,也更難被逆向工程。
  預檢(Preverify):在java平臺上對處理后的代碼進行預檢。
混淆流程圖:

Android混淆
Proguard讀入input jars(or wars,zip or directories),經過四個步驟生成處理之后的jars(or wars,ears,zips or directories),Optimization步驟可選擇多次進行。
為了確定哪些代碼應該被保留,哪些代碼應該被移除或混淆,需要確定一個或多個Entry Point。Entry Point經常是帶有main methods,applets,midlets的classes,它們在混淆過程中會被保留。
Proguard的幾個步驟如何處理Entry Points。
 ?。?).在壓縮階段,Proguard從上述Entry Points開始遍歷搜索哪些類和類成員被使用。其他沒有被使用的類和類成員會移除。
 ?。?).在優(yōu)化階段,Proguard進一步設置非Entry Point的類和方法為private、static和final來進行優(yōu)化,不使用的參數(shù)會被移除,某些方法會被標記為內聯(lián)。
 ?。?).在混淆階段,Proguard重命名非Entry Points的類和類成員。
 ?。?).預檢階段是唯一沒有觸及Entry Points的階段。

三、Android Studio默認的混淆方案及字段解讀

1.開啟混淆    

在build.gradle文件內相應的構建類型中添加minifyEnabled true即可。
除了minifyEnable屬性外,還有用于定義ProGuard規(guī)則的proguardFiles屬性:

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'

官方文檔介紹:
  getDefaultProguardFile('proguard-android.txt')方法可從Android SDK tools/proguard/文件夾獲取默認的ProGuard設置。要想做進一步的代碼壓縮,請嘗試使用位于同一位置的proguard-android-optimize.txt文件。它包括相同的Proguard規(guī)則,但還包括其他在字節(jié)碼一級(方法內和方法間)執(zhí)行分析的優(yōu)化,以進一步減少APK大小和幫助提高其運行速度。
proguard-rules.pro文件用于添加自定義Proguard規(guī)則。默認情況下,該文件位于模塊根目錄(build.gradle文件旁),內容為空。
在gradle 2.2之后,defaultProguardFile沒有使用sdk目錄下的proguard-android.txt,而是使用了gradle自帶的proguard-android.txt,不同的gradle版本帶有不同的默認混淆文件,在項目根目錄的build/intermediates/proguard-files/proguard-android.txt-2.3.1。
混淆配置文件不檢查規(guī)則是否重復,如果兩條規(guī)則沖突,則采用白名單的,比如設置了開啟優(yōu)化和不優(yōu)化兩個選項后,不論順序,最終都會執(zhí)行不優(yōu)化的操作。

2.構建輸出

構建時Proguard都會輸出下列文件:(build之后)
 ?。?)dump.txt --- 說明APK中所有類文件的內部結構
 ?。?)mapping.txt --- 提供原始與混淆過的類、方法和字段名稱之間的轉換
 ?。?)seeds --- 列出未進行混淆的類和成員
 ?。?)usage.txt --- 列出從APK移除的代碼
這些文件保存在/build/outputs/mapping/release目錄下。

3.解碼混淆過的堆棧追蹤

使用混淆后,保存好mapping文件,程序csh時通過腳本進行解碼。
retrace工具位于/tools/proguard/目錄下,解碼命令為:

retrace.bat|retrace.sh [-verbose] mapping.txt [<stacktrace_file>]

4.默認的混淆方案及字段解讀

(1)不適用大小混寫類名

-dontusemixedcaseclassnames

默認情況下混淆的類名可以包含大小寫字符的混合
(2)不忽略公共類庫

-dontskipnonpubliclibraryclasses

指定不去忽略非public的library classes。從Proguard 4.5開始,是默認的設置。
(3)不優(yōu)化指定的文件與不預檢驗

-dontoptimize
-dontpreverify

默認optimize和preverify選項是關閉的,因為Android的dex并不想Java虛擬機需要optimize(優(yōu)化)和previrify(預檢)兩個步驟。
(4)指定哪個屬性不要混淆,可一次指定多個屬性

-keeppattributes [attribute_filter]

通常Exceptions,Signature,Deprecated,SourceFile,SourceDir,LineNumberTable,LocalVariableTable,LocalVariableTypeTable,Synthetic,EnclosingMethod,RuntimeVisibleAnnotations,RuntimeInvisibleAnnotations,RuntimeVisibleParameterAnnotations,RuntimeInvisibleParameterAnnotations,AnnotationDefault屬性需要被保留,根據(jù)項目具體使用情況保留。

gradle默認的keepattributes屬性不全,只保留了Annotations,Signature,InnerClasses,EnclosingMethod,為了混淆之后定位csh代碼方便,需要在proguard_rules.pro中手動添加拋出異常時保留代碼行號,并且重命名超出異常時的文件名稱,這樣能方便定位問題:
#拋出異常時保留代碼行號

 -keeppattributes SourceFile,LineNumberTable

#重命名拋出異常時的文件名稱

  -renamesourcefileattribute SourceFile

keep選項制定了哪些類,哪些方法不被混淆,從而保證了程序的正常運行。
keep用法有6種:
  (1)-keep(names)選項 指定類和類成員(變量和方法)不被混淆
    -keep [,modifier,...] class_specification
    //指定類名不被改變

      -keep public class com.google.vending.licensing.ILicensingService

    //指定使用了Keep注解的類和類成員都不被改變

      -keep @android.support.annotation.Keep class * {*;}

  (2)-keepclassmembers(names) 指定類成員不被混淆,類名會被混淆

      //keep setters in views 使得animations仍然能夠工作

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

  (3)-keepclasseswithmembers(names) 指定類和類成員都不被混淆
    -keepclasseswithmembers [,modifier,...] class_specification
    //包含native方法的類名和native方法都不能被混淆,如果native方法未被調用,則被移除。由于native方法與對應so庫中的方法名稱對應,方法名被混淆會導致調用出現(xiàn)問題,所以native方法不能被混淆。

-keepclasseswithmembernames class * {
         native <methods>;
}

不帶names的選項為From being removed or renames,既不會被移除或重命名,即使類或類成員未被使用。帶有names的選項為From being renamed,不會被重命名,如果是無用的類或類成員,會被移除,移除是指在壓縮(Shrinking)時是否會被刪除。 
通用Options:
  (1)-verbose 打印混淆詳細信息
 ?。?)-dontnote選項:指定不去輸出打印該類產生的錯誤或遺漏
    -dontnote com.android.vending.licensing.ILicensingService
    -dontnote android.support.**
 ?。?)-dontwarn選項:指定不去warn unresolved references和其他重要的problem
    -dontwarn android.support.**
如上面(2)(3)所示,android.support的libraries需要保留。

四、自定義混淆文件

1.Filters

?      匹配一個字符
*       匹配一個名字,除了目錄外分隔符外的任意部分
**      匹配任意名,可能包含任意路徑分隔符
!     排除
<field>        匹配類中的所有字段
<method>   匹配類中所有的方法
<init>          匹配類中所有的構造函數(shù)

-keep class com.lily.test.**          本包和所包含子包下的類名都保持
-keep class com.lily.test.*           保持該包下的類名
-keep class com.lily.test.** {*;}    保持包和子包的類名和里面的內容均不被混淆

如果要保留一個類中的內部類不被混淆則需要用$符號。

2.-assumenosideeffects指令:

assumeosideeffects是Optimization過程中的選項,所以為保證指令的有效,需要開啟optimization。這個指令的含義是Proguard會在optimization過程中刪除對這些方法的調用,需要注意:當你知道你在做什么的時候才能使用它。

3.一個自定義文件

Android混淆

#代碼混淆壓縮比,在0~7之間
-optimizationpasses 5# 
#混淆時不適用大小寫混合,混合后的類名為小寫
-dontusemixedcaseclassnames

#指定不去忽略非公共庫的類
-dontskipnonpubliclibraryclasses

#不做預校驗,preverify是proguard的四個步驟之一,Android不需要precerify,去掉這一步能夠加快混淆速度。
-dontpreverify

-verbose

#google推薦算法
-optimizations !code/simplification/arithmetic,!code/simplication/cast,!field/*,!class/mergin/*

#避免混淆Annotation、內部類、泛型、匿名類
-keepattributes *Annotation*,InnerClasses,Signature,EnclosingMethod

#重命名拋出異常時的文件名稱
-renamesourcefileattribute SourceFile

#拋出異常時保留代碼行號
-keepattributes SourceFile,LineNumberTable

#處理support包
-dontnote android.support.**
-dontwarn android.support.**

#保留四大組件,自定義的Application等這些類不被混淆
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * entends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService

#保留本地native方法不被混淆
-keepclasseswithmembernames class * {
native <methods>}

#保留枚舉類不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}

#第三方jar包不被混淆
-keep class com.github.test.** {*;}

#保留自定義的Test類和類成員不被混淆
-keep class class.lily.Test {*;}

#保留自定義的xlog文件夾下面的類、類成員和方法不被混淆
-keep class com.text.xlog.** {<fields>;<methods>;
}

#assume no side effects;刪除android.util.Log輸出的日志
-assumenosideeffects class android.util.Log {
public static *** v(...);
public static *** d(...);
public static *** i(...);
public static *** w(...);
public static *** e(...);
}

#保留keep注解的類名和方法
-keep,allowobfuscation @interface android.support.annotation.Keep
-keep @android.support.annotation.Keep class *
-keepclassmember class * {
@android.support.annotation.Keep *;
}

Android混淆

五、常用到的不混淆

1.jni方法不混淆

jni方法不混淆,因為方法需要和native方法保持一致。

-keepclasseswithmembernames class * { 
    # 保持native方法不被混淆
    native <methods>;
}

2.反射不混淆

反射用到的類不混淆(否則混淆可能出現(xiàn)問題)。

-keepatrributes EnclosingMethod

3.AndroidMainfest中的類不混淆

AndroidMainfest中的類不混淆,所以四大組件和Application的子類和Framework層下所有的類默認不會進行混淆。自定義的View默認也不會被混下,所以排除自定義View,或者四大組件被混淆的規(guī)則在ndroid Studio中無需加入的,下面是兼容性比較高的規(guī)則:

Android混淆

-keep public class * extends android.app.Fragment
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Service
-keep public class * extends android.content。BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * entends android.app.backup.BackupAgentHelper
-keep public class * entends android.preference.Preference

Android混淆

4.JSON對象類不混淆

服務器交互時,使用GSON、fastjson等框架解析服務端數(shù)據(jù)時,所寫的JSON對象類不混淆,否則無法將JSON解析成對應的對象。

5.第三方開源或SDK包

使用第三方開源庫或者引用其他第三方的SDK包時,如果有特別要求,也需要在混淆文件中加入對應的混淆規(guī)則。

6.WebView的JS調用的接口方法不混淆

有用到WebView的JS調用也需要保證寫的接口方法不混淆,原則和第一條一樣。

-keepclassmembers classs fqcn.of.javascript.interface.for.webview {
    public *;
}

7.Parcelable的子類和Creator靜態(tài)成員變量不混淆

Parcelable的子類和Creator靜態(tài)成員變量不混淆,否則會產生Android.os.BadParcelableException異常;

-keep class * implements Android.os.Parcelable { 
    # 保持Parcelable不被混淆
    public static final Android.os.Parcelable$Creator *;
}

8.enum類型

使用enum類型時需要注意避免以下兩個方法混淆,因為enum類的特殊性,以下兩個方法會被反射調用。

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

9.注解不混淆

-keepatrributes *Annotation*
-keep class * extends java.lang.annotation.Annotation {*;}

10.泛型不混淆

-keepattributes Signature

11.內部類不混淆

-keepattributes InnerClasses

六、第三方混淆參考規(guī)則

1.Gson

-dontwarn com.google.**
-keep class com.google.gson.** {*;}

2.otto

-keepattributes *Annotation*
-keepclassmembers class ** {
    @com.squareup.Subscribe public *;
    @com.squareup.otto.Produce public *;
}

3.universal-p_w_picpath-loader

-dontwarn com.nostra13.universalp_w_picpathloader.**
-keep class com.nostra13.universalp_w_picpathloader.** {*;}

4.友盟統(tǒng)計

Android混淆

-keepclassmembers class * {
    public <init> (org.json.JSONObject);
}
#友盟統(tǒng)計5.0.0以上SDK需要
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
#友盟統(tǒng)計R.java刪除問題
-keep public class com.gdhbgh.activity.R$*{
    public static final int *;
}

Android混淆

5.OkHttp

-dontwarn com.squareup.okhttp.**
-keep class com.squareup.okhttp.** {*;}
-keep interface com.squareup.okhttp.** {*;}
-dontwarn okio.**

6.nineoldandroids

-dontwarn com.nineoldandroids.*;
-keep class com.nineoldandroids.** {*;}

7.支付寶

Android混淆

-keep class com.alipay.android.app.IAlixPay{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback{*;}
-keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;}
-keep class com.alipay.sdk.app.PayTask{
    public *;
}
-keep class com.alipay.sdk.app.AuthTask{
    public *;
}

Android混淆

8.Socket.io

-keep class socket.io-client.
-keepclasswithmembers,allowshrinking class socket.io-client.* {*;}
-keep class io.socket.
-keepclasseswithmembers,allowshrinking class io.socket.* {*;}

9.JPUSH

-dontwarn cn.jpush.**
-keep class cn.jpush.** {*;}

# protobuf(jpush依賴)
-dontwarn com.google.**
-keep class com.google.protobuf.** {*;}

10.友盟分享

Android混淆

-dontwarn com.umeng.**
-dontwarn com.tencent.weibo.sdk.**

-keep public interface com.tencent.**
-keep public interface com.umeng.socialize.**
-keep public interface com.umeng.socialize.sensor.**
-keep public interface com.umeng.scrshot.**

-keep public class com.umeng.socialize.* {*;}

-keep class com.umeng.scrshot.**
-keep public class com.tencent.** {*;}
-keep class com.umeng.socialize.sensor.**
-keep class com.umeng.socialize.handler.**
-keep class com.umeng.socialize.handler.*
-keep class com.tencent.mm.sdk.modelmsg.WXMediaMessage {*;}
-keep class com.tencent.mm.sdk.modelmsg.** implements com.tencent.mm.sdk.modelmsg.WXMediaMessage$IMediaObject {*;}

-keep class im.yixin.sdk.api.YXMessage {*;}
-keep class im.yixin.sdk.api.** implements im.yixin.sdk.api.YXMessage$YXMessageData{*;}

-keep class com.tencent.** {*;}
-dontwarn com.tencent.**
-keep public class com.umeng.soexample.R$*{
public static final int *;
}
-keep class com.tencent.open.TDialog$*
-keep class com.tencent.open.TDialog$* {*;}
-keep class com.tencent.open.PKDialog
-keep class com.tencent.open.PKDialog {*;}
-keep class com.tencent.open.PKDialog$*
-keep class com.tencent.open.PKDialog$* {*;}

-keep class com.sina.** {*;}
-dontwarn com.sina.**
-keep class com.alipay.share.sdk.** {*;}

Android混淆

七、常見的一些問題

1.網(wǎng)絡層混淆

一般網(wǎng)絡層都不進行混淆,可以經過劃分包后直接不混淆網(wǎng)絡層的包:

-keep class com.xxx.xxx.http.** {*;}

2.數(shù)據(jù)模型混淆

-keep class * implements java.io.Serializable {*;}
-keepclassmembers class * implements java.io.Serializable {*;}

有時候上面的這種方式可能會導致應用卡住,沒有任何錯誤提示,所以建議采用分包模式,吧所有bean放在同一個包中,直接對該包加白名單。

-keep class com.xxx.xxx.domain.xx {*;}

3.XML映射混淆

如果遇到一些空間無法Inflate,報NullPointException,比如ListView,NavigationView等等

-keep class *.** {*;}

4.混淆規(guī)則編寫方法

如果混淆后報錯,通過retrace后找到錯誤的問題后可以直接編寫規(guī)則來去掉混淆,但是如果報的錯誤莫名其妙,而且報錯的類沒有混淆,那么可以采用極端的方法,加入下面的規(guī)則:

-keep class *.** {*;}

這條規(guī)則表示不混淆所有類及其中所有代碼,加了這條規(guī)則之后,還不能運行表示是其他問題,例如注解,內部類等等,可以運行后,可以通過反編譯,尋找所有包名,記錄下來,吧上述規(guī)則改為:

-keep class android.** {*;}
-keep class com.** {*;}
-keep class org.** {*;}

一個個去掉檢查是否有報錯,例如查到

-keep class com.** {*;}

加了就沒有錯誤,則可以繼續(xù)一級級往下檢查。
但要注意,有時候可能是幾個包混合問題。


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI