Android代码混淆方法,Android 代码混淆零基础入门
內容提要
本篇文章主要有三個部分,讓讀者讀完后能自己寫規則混淆項目
對Android代碼怎么開啟混淆做一個簡單的介紹。
對混淆規則做一個簡單介紹;
在混淆過后Crash日志反推代碼工具retrace.bat、可視化反推工具GUI說明。
對混淆的一個簡單介紹:
Android SDK 自帶了混淆工具Proguard。它位于SDK根目錄\tools\proguard下面。如果開啟了混淆,Proguard默認情況下會對所有代碼,包括第三方包都進行混淆,可是有些代碼或者第三方包是不能混淆的,這就需要我們手動編寫混淆規則來保持不能被混淆的部分。
混淆有幾個作用:
【優化】它能優化java的字節碼,使程序運行更快;
【壓縮】最直觀的就是減少App大小,在混淆過程中它會找出未被使用過的類和類成員并刪除他們;
【混淆】這個功能使我們的java代碼中的類、函數、變量名隨機變成無意義的代號形如:a,b,c...之類的,即使我們的APP即使被反編譯,也不容易理解了。
上面這幾個功能都是默認打開的,要關閉他們只需配置規則:
-dontshrink :關閉壓縮;
-dontoptimize:關閉優化;
-dontobfuscate:關閉混淆。
寫在開始之前:
本編使用Android Studio工具作為開發環境,Eclipse類似;
Android Studio新建項目時會在項目根目錄下自動生成一個混淆配置文件:
proguard-rules.pro;【Eclipse 下面的文件名為:proguard.cfg或者proguard.txt】里面的內容規則都是一樣的;
1.現在開始混淆:
第一步:開啟混淆,編輯項目配置文件build.gradle(Module: app)
*** minifyEnabled true;這個選項的意思為開啟混淆;
*** proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro';//配置文件**
'proguard-android.txt' 是AndroidStudio默認自動導入的規則,這個文件位于Android SDK根目錄\tools\proguard\proguard-android.txt。這里面是一些比較常規的不能被混淆的代碼規則。'proguard-rules.pro'是針對我們自己的項目需要特別定義混淆規則,它位于項目根目錄下面,里面的內容需要我們自己編寫
zipAlignEnabled true 這個在打包時需要設置為true,能優化我們的java字節碼,提高運行效率;
build.gradle編輯完成后如下圖所示:(表示發布的release包,會按照指定規則來進行混淆)
android {
compileSdkVersion 23
buildToolsVersion "24.0.1"
defaultConfig {
}
buildTypes {
release { //主要看這部分:
zipAlignEnabled true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
....
}
第二步:現在就可以開始編輯自己的規則了
下面是一個比較簡單的proguard-rules.pro文件內容,做完這些就可以打包發布版本了
1.png
將混淆過的APK反編譯,你會發現里面的代碼變成了a、b、c之類的沒有意義的代號了,而且APK也變小了。
2.重點來了【規則介紹】
現在對混淆規則做一個介紹:
先來看一看Android studio 默認包含的規則文件(proguard-android.txt 文件)里面的類容,我們選出幾個重點說明:
#混淆時不生成大小寫混合的類名
-dontusemixedcaseclassnames
#不忽略非公共的類庫
-dontskipnonpubliclibraryclasses
#混淆過程中打印詳細信息
-verbose
#關閉優化
-dontoptimize
#不預校驗
-dontpreverify
# Annotation注釋不能混淆
-keepattributes *Annotation*
#對于NDK開發 本地的native方法不能被混淆
-keepclasseswithmembernames class * {
native ;
}
#保持View的子類里面的set、get方法不被混淆(*代替任意字符)
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
#保持Activity子類里面的參數類型為View的方法不被混淆,如被XML里面應用的onClick方法
# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
#保持枚舉類型values()、以及valueOf(java.lang.String)成員不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
#保持實現Parcelable接口的類里面的Creator成員不被混淆
-keepclassmembers class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator CREATOR;
}
#保持R類靜態成員不被混淆
-keepclassmembers class **.R$* {
public static ;
}
#不警告support包中不使用的引用
-dontwarn android.support.**
-keep class android.support.annotation.Keep
-keep @android.support.annotation.Keep class * {*;}
#保持使用了Keep注解的方法以及類不被混淆
-keepclasseswithmembers class * {
@android.support.annotation.Keep ;
}
#保持使用了Keep注解的成員域以及類不被混淆
-keepclasseswithmembers class * {
@android.support.annotation.Keep ;
}
-keepclasseswithmembers class * {
@android.support.annotation.Keep (...);
}
上面默認的規則中指示了些需要保持不能別混淆的代碼,包括:
繼承至Android組件(Activity, Service...)的類。
自定義控件,繼承至View的類(被xml文件引用到的,名字已經固定了的)
enum 枚舉
實現了 android.os.Parcelable 接口的
Android R文件
數據庫驅動...
Android support 包等
Android 的注釋不能混淆
-keepattributes *Annotation*
對于NDK開發 本地的native方法不能被混淆
-keepclasseswithmembernames class * { native ; }
對于特定的項目還有很多不能被混淆的,需要我們自己寫規則來指示,將在下面來說明:
對一些符號做一些說明
現在來看一下需要我們自己編寫的proguard-rules.pro:
#壓縮級別0-7,Android一般為5(對代碼迭代優化的次數)
-optimizationpasses 5
#不使用大小寫混合類名
-dontusemixedcaseclassnames
#混淆時記錄日志
-verbose
#不警告org.greenrobot.greendao.database包及其子包里面未應用的應用
-dontwarn org.greenrobot.greendao.database.**
-dontwarn rx.**
-dontwarn org.codehaus.jackson.**
......
#保持jackson包以及其子包的類和類成員不被混淆
-keep class org.codehaus.jackson.** {*;}
#--------重要說明-------
#-keep class 類名 {*;}
#-keepclassmembers class 類名{*;}
#一個*表示保持了該包下的類名不被混淆;
# -keep class org.codehaus.jackson.*
#二個**表示保持該包以及它包含的所有子包下的類名不被混淆
# -keep class org.codehaus.jackson.**
#------------------------
#保持類名、類里面的方法和變量不被混淆
-keep class org.codehaus.jackson.** {*;}
#不混淆類ClassTwoOne的類名以及類里面的public成員和方法
#public 可以換成其他java屬性如private、public static 、final等
#還可以使表示構造方法、表示方法、表示成員,
#這些前面也可以加public等java屬性限定
-keep class com.dev.demo.two.ClassTwoOne {
public *;
}
#不混淆類名,以及里面的構造函數
-keep class com.dev.demo.ClassOne {
public ();
}
#不混淆類名,以及參數為int 的構造函數
-keep class com.dev.demo.two.ClassTwoTwo {
public (int);
}
#不混淆類的public修飾的方法,和private修飾的變量
-keepclassmembers class com.dev.demo.two.ClassTwoThree {
public ;
private ;
}
#不混淆內部類,需要用$修飾
#不混淆內部類ClassTwoTwoInner以及里面的全部成員
-keep class com.dev.demo.two.ClassTwoTwo$ClassTwoTwoInner{*;}
......
對一些規則的解釋:
-keepattributes {name}??保護給定的屬性不被混淆,
如:-keepattributes *Annotation*
-dontwarn {name}??不要警告指定庫中找不到的引用。混淆在默認情況下會檢查每個庫的引用是否正確,但是有些第三方庫里面會有用不到的類,有些沒有正確引用,所以需要對第三方庫取消警告 否則會報錯,而且有可能混淆時間會很長。
如:-dontwarn org.codehaus.jackson.**
-keep {Modifier} {class_specification}??保留指定的類名、類成員不被混淆
-keepclassmembers {modifier} {class_specification} ??保留指定的類成員不被混淆
-keepclasseswithmembers {class_specification} ??保留指定的類名、類成員不被混淆
-keepnames {class_specification} ??保留指定的類名、類成員的名稱不被混淆
-keepclasseswithmembernames {class_specification} ??保留指定的類名、類成員名稱不被混淆(如果存在的話)
3.混淆后根據Crash崩潰日志反推代碼
成功打包后會在目錄*** app\build\outputs\mapping\release ***下生成幾個文件:
2.png
說明:
dump.txt 混淆后類的內部結構說明;
mapping.txt 混淆前與混淆后名稱對應關系;
seeds.txt 經過了一系列keep語句的保持,沒有被混淆的類,成員的名稱列表文件。
usage.txt 經過壓縮后被刪除的沒有使用的代碼,方法...等的名稱的列表文件
retrace工具:
混淆反推工具為retrace.sh(Mac平臺)或者retrace.bat(Windows平臺)
該工具在sdk根目錄\tools\proguard\bin\retrace.sh
(Windows平臺類似);
命令格式:
*** ./retrace.sh [mapping.txt目錄] [崩潰日歷目錄]
(Windows平臺:retrace.bat [mapping.txt目錄] [崩潰日歷目錄]) ***
第一步:保存Crash日志如下:截取日志保存為txt文件如:bug.txt。
03-21 03:09:32.389: E/AndroidRuntime(3582): FATAL EXCEPTION: main
03-21 03:09:32.389: E/AndroidRuntime(3582): Process: com.dev.demo, PID: 3582
03-21 03:09:32.389: E/AndroidRuntime(3582): java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.two.b.b(Unknown Source)
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.a.a.printTest(Unknown Source)
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.MainActivity.i(Unknown Source)
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.MainActivity.a(Unknown Source)
03-21 03:09:32.389: E/AndroidRuntime(3582): at com.dev.demo.MainActivity$1.onClick(Unknown Source)
第二步:刪掉Crash日志中03-21 03:09:32.389: E/AndroidRuntime(3582):部分,否則無法反推還原。刪除后如下:
FATAL EXCEPTION: main
Process: com.dev.demo, PID: 3582
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
at com.dev.demo.two.b.b(Unknown Source)
at com.dev.demo.a.a.printTest(Unknown Source)
at com.dev.demo.MainActivity.i(Unknown Source)
at com.dev.demo.MainActivity.a(Unknown Source)
at com.dev.demo.MainActivity$1.onClick(Unknown Source)
第三步:打開命令窗口輸入命令:如:*** ./retrace.sh mapping.txt bug.txt ***
如下:
3.png
按確認后得到結果如下:
FATAL EXCEPTION: main
Process: com.dev.demo, PID: 3582
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean java.lang.String.equals(java.lang.Object)' on a null object reference
at com.dev.demo.two.ClassTwoThree.void test()(Unknown Source)
at com.dev.demo.one.ClassOneOne.void printTest()(Unknown Source)
at com.dev.demo.MainActivity.void printWifi()(Unknown Source)
at com.dev.demo.MainActivity.void access$000(com.dev.demo.MainActivity)(Unknown Source)
at com.dev.demo.MainActivity$1.void onClick(android.view.View)(Unknown Source) ```
除了有retrace.sh工具外還有可視化工具,和retrace.sh同目錄下proguardgui.sh,這是一塊Progurad的可視化工具:
(Windows平臺類似)

>有時為了Crash日志更容易定位可以在規則里面添加:
-keepattributes SourceFile, LineNumberTable
**這樣Crash日志里面就能保留類名稱 和行號了**
**好了,所有終于完了,希望能幫助到你!謝謝!**
***
總結
以上是生活随笔為你收集整理的Android代码混淆方法,Android 代码混淆零基础入门的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java调用qq接口_用java代码怎么
- 下一篇: 怎么把html转换成jpg6,html转