1. 程式人生 > >ProGuard進行混淆程式碼原理初探

ProGuard進行混淆程式碼原理初探

ProGuard充當Java類檔案的縮小,優化,混淆,先行檢驗的角色。

ProGuard執行guo'cheng

總體原理描述

<1>縮小步驟找出並移除無用類,欄位,方法和屬性。優化過程分析和優化方法的位元組碼。
<2>混淆過程用短而無意義的名字重新命名餘下的類,欄位,和方法。
<3>第一個的步驟使程式碼變得更加小且效率和更難進行逆行工程。最後一個先行檢驗的步驟新增先行檢驗資訊到類,這個類是java微型版要求並且能提高Java6的啟動速度。

注意:

每一個步驟都是可選的。例如,ProGuard也能夠僅僅被用來列出應用程式dead code,或者先行檢驗類檔案在Java6高效果執行。
ProGuard通常讀取輸入Jars(或者wars,ears,zips,或者目錄樹),然後收縮,優化,混淆和先行檢驗它們。具有選擇性的是,它可以執行多個優化通道,每一個通常遵循另一個收縮步驟。ProGuard把處理結果寫到到一個或多個輸出jars(或者wars,ears,zips,或者目錄樹)。輸入可能包含資原始檔,其名稱和內容可選擇的被更新以對映到混淆的類名稱。
ProGuard要求 library jars(或者wars,ears,zips,或者目錄樹)指定輸入Jars。這些本質上是你將需要編譯程式碼時的libraries。ProGuard用他們去重建那些需要適當處理的類的依賴關係。Library jars本身是保持不變的,你仍然應該把它們放到你的最終應用程式的類路徑。
本《使用手冊》describes keep 選項的必要性和例項部分提供了很多例子。

Entry points(切入點)


為了確保哪些程式碼被儲存,哪些被廢棄或混淆,你必須為你的程式碼指定一個或多個切入點。
以下切入點通常與類的主方法,applet和midlets相關
1.在收縮步驟,ProGuard從那些種子和遞迴確定所使用的類和類成員開始。所有其它類和類成員都會被丟棄。
2.在優化步驟,ProGuarad進一步優化程式碼。在其他的優化中,沒有切入點的類和方法可以是private,static or final,且無用的引數可以被移除和一些方法可能是inline的。
3.在混淆步驟,ProGuard重新命名沒有切入點的類和類成員。在整個過程中,保持切入點確保它們冉冉可以訪問它們原來的名字。
4.在先行檢驗步驟,是不知道入口點的唯一步驟。

Reflection(反射)

反射和introspection為任何自動執行的程式碼提出了特殊的問題。在ProGuard中,你程式碼中的類和類成員被建立或者動態呼叫(即是通過名字),不得不指定為一個切入點。
例如,Class.forName()構造器可能在執行時涉及到任何類。通常不可能預見哪些類必須保留(用它們的原始名稱),因為類名可能會從配置檔案中讀取。例如,你因此必須指定他們在你的ProGuard配置,用簡單的 -keep選項確保其不被混淆。

ProGuard在下列情況下檢測並幫你處理。

1.Class.forName("SomeClass")
2.SomeClass.class
3.SomeClass.class.getField("someField") 4.SomeClass.class.getDeclaredField("someField") 5.SomeClass.class.getMethod("someMethod", new Class[] {}) 6.SomeClass.class.getMethod("someMethod", new Class[] { A.class }) 7.SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class }) 8.SomeClass.class.getDeclaredMethod("someMethod", new Class[] {}) 9.SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class }) 10.SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class }) 11.AtomicIntegerFieldUpdater.newUpdater(SomeClass.class, "someField") 12.AtomicLongFieldUpdater.newUpdater(SomeClass.class, "someField") 13.AtomicReferenceFieldUpdater.newUpdater(SomeClass.class, SomeType.class, "someField")

當然,類和類成員的名字不同,但結構確實應為ProGuard所確認那樣。
引用的類和類成員被保留在收縮階段,字串引數在混淆階段正確的更新。
為了獲得正確的結果,您至少應該熟悉您正在處理的程式碼。混淆程式碼執行很多反射,可能要要求實驗和遇到各種錯誤,特別是在沒有程式碼內部的必要資訊的情況。

ReTrace是ProGuard混淆堆疊的一個陪同工具。

當一個模糊的程式丟擲一個異常,產生結果的堆疊一般都不是有用的資訊。類名和方法名已經被無意義的短字串替代。原始檔名和行號也同樣被忽略。雖然這可能是故意的,但在除錯問題時候也坑能帶來不便。
混淆原理
ReTrace可以閱讀混淆的堆疊跟蹤和恢復它到一種看起來沒有混淆時候的狀態。恢復以 mapping file為基礎,ProGuard 可以在混淆時寫完. mapping file連線初始的類名和類成員名字和它們混淆後的名字。

ProGuard工具縮小,優化,和混淆你的程式碼通過刪除無用的程式碼和重新命名類、欄位和語義模糊的的命名。這樣就會產生一個更小的apk檔案,更難進行逆向工程。在你應用程式涉及安全敏感的特性時,新增證書的時候,使用ProGurad進行混淆是很重要的。
ProGuard是整合到安卓構建系統的,所以你不必手動呼叫它。ProGuard僅僅當你在release模式下構建時執行,所以你不必處理混淆程式碼當你在debug模式構建你的應用程式。ProGuard在執行時是可選的,但是強烈推薦執行它。

啟用ProGuard (Gradle Builds)

minifyEnabled

這個屬性是relase模式下表示ProGuard是否啟動的標記,下面這段程式碼要載入moudles的build.gradle這個檔案中

getDefaultProguardFile(‘proguard-android.txt’)

獲取安卓sdk tools/proguard 資料夾下面的預設ProGuard配置
proguard-android-optimize.txt這個檔案仍然可以使用,它和普通的有同樣的規則,但是能夠優化配置,在位元組碼的級別上去優化你的app。
proguard-rules.pro 在moudle的根目錄
這個例子直接給proguardFiles新增proguard-rules-new.pro 和給flavor2新增.other-rules.pro

 android {
   ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
            'proguard-rules.pro'
        }
    }
  }
android {
   ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),
            'proguard-rules.pro', 'proguard-rules-new.pro'
        }
    }
   productFlavors {
        flavor1 {
        }
        flavor2 {
            proguardFile 'other-rules.pro'
        }
    }
 }

Configuring ProGuard
在某些情況下,ProGuard配置檔案裡面的預設配置就足夠了。然而,很多情況下,ProGuard很難正確的分析並可能移除它所認為的沒用的程式碼,但你應用程式實際是需要的。
1.一個只在AndroidManifest.xml檔案引用的類
2.通過JNI呼叫的方法
3.動態引用的欄位或方法
一般情況下預設的ProGuard配置檔案儘量去覆蓋,但是你可能遭遇到一個ClassNotFoundException異常,發生在當ProGuard除掉你應用程式呼叫的一整個類。
通過以下方法確保某個類不被ProGuard移除,舉例
-keep public class
這理由很多選項和使用-keep 選項時需要考慮的,所以熱烈建議你讀ProGuard Manual 瞭解更多關於自定義配置檔案的資訊。Keep選項和示例部分的概述特別有用。ProGuard Manual的Troubleshooting(故障排除)概述了其他常見問題,你可能碰到的程式碼被移除的問題。
Decoding Obfuscated Stack Traces
(解碼混淆的堆疊跟蹤)
當你的混淆的程式碼輸出一個堆疊跟蹤,方法名被混淆,這導致除錯變難,這不是不可能的。幸運的是,不管ProGuard什麼時候執行,它都輸出一個mapping.txt檔案,這個檔案給你展示了與混淆後的名字匹配的那些初始類,方法和欄位的名。
Window作業系統的指令碼retrace.bat 或者Linus作業系統的指令碼 retrace.sh 或者Mac OS X 能夠將混淆的堆疊跟蹤轉換成可閱讀的堆疊跟蹤。這個指令碼檔案位於目錄/tools/proguard/ 。執行retrace 工具的語法如下:

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

For example:

retrace.bat -verbose mapping.txt obfuscated_trace.txt

如果沒有指定的值, retrace 工具將從標準輸入流讀取資訊。
Debugging considerations for published applications(已釋出應用程式的注意事項)
儲存每一次釋出產生的檔案 mapping.txt 。為每一個釋出構建保留 mapping.txt 的副本, 確保你可以除錯一個問題如果使用者遇到Bug並提交一個混淆的堆疊跟蹤。專案的mapping.txt 檔案在你每次進行釋出構建的時候重寫 ,所以你必須注意儲存你所需要儲存的版本。在Eclipse中, 檔案儲存在在 /bin/proguard/. 在Android Studio中, 檔案儲存在app build/outs/ 資料夾.
例如, 假如你釋出一個應用程式並繼續為新版本開發應用程式的新功能。你不久後用ProGuard再做一個釋出構建. 此構建會覆蓋以前的mapping.txt 檔案. 一個使用者提交一個來自當前釋出的應用程式且包含堆疊跟蹤的bug報告. 你不再有辦法除錯使用者的堆疊跟蹤, 因為與使用者裝置上的版本相關聯的 mapping.txt 檔案已不復存在.在一些情況下,你的mapping.txt 檔案可以被覆蓋 , 所以確保你已經儲存了每一個預期需要除錯的檔案副本
如何儲存mapping.txt檔案是你的決定。例如,您可以重新命名檔案以包含version或bulid number,也可以版本控制它們和原始碼一起。