防止 jar 包被反編譯
1.隔離Java程式
最簡單的方法就是讓使用者不能夠訪問到Java Class程式,這種方法是最根本的方法,具體實現有多種方式。例如,開發人員可以將關鍵的Java Class放在伺服器端,客戶端通過訪問伺服器的相關介面來獲得服務,而不是直接訪問Class檔案。
2.Part3對Class檔案進行加密
為了防止Class檔案被直接反編譯,許多開發人員將一些關鍵的Class檔案進行加密,例如對註冊碼、序列號管理相關的類等。在使用這些被加密的類之前,程式首先需要對這些類進行解密,而後再將這些類裝載到JVM當中。這些類的解密可以由硬體完成,也可以使用軟體完成。
在實現時,開發人員往往通過自定義ClassLoader類來完成加密類的裝載(注意由於安全性的原因,Applet不能夠支援自定義的 ClassLoader)。自定義的ClassLoader首先找到加密的類,而後進行解密,最後將解密後的類裝載到JVM當中。
在這種保護方式中,自定義的ClassLoader是非常關鍵的類。由於它本身不是被加密的,因此它可能成為黑客最先攻擊的目標。如果相關的解密金鑰和演算法被攻克,那麼被加密的類也很容易被解密。
3.Part4轉換成原生代碼
將程式轉換成原生代碼也是一種防止反編譯的有效方法。因為原生代碼往往難以被反編譯。開發人員可以選擇將整個應用程式轉換成原生代碼,也可以選擇關鍵模組轉換。如果僅僅轉換關鍵部分模組,Java程式在使用這些模組時,需要使用JNI技術進行呼叫。
當然,在使用這種技術保護Java程式的同時,也犧牲了Java的跨平臺特性。對於不同的平臺,我們需要維護不同版本的原生代碼,這將加重軟體支援和維護的工作。不過對於一些關鍵的模組,有時這種方案往往是必要的。
為了保證這些原生代碼不被修改和替代,通常需要對這些程式碼進行數字簽名。在使用這些原生代碼之前,往往需要對這些原生代碼進行認證,確保這些程式碼沒有被黑客更改。如果簽名檢查通過,則呼叫相關JNI方法。
4.Part5程式碼混淆
程式碼混淆是對Class檔案進行重新組織和處理,使得處理後的程式碼與處理前程式碼完成相同的功能(語義)。但是混淆後的程式碼很難被[反編譯,即[反編譯後得出的程式碼是非常難懂、晦澀的,因此[反編譯人員很難得出程式的真正語義。
從理論上來說,黑客如果有足夠的時間,被混淆的程式碼仍然可能被破解,甚至目前有些人正在研製反混淆的工具。但是從實際情況來看,由於混淆技術的多元化發展,混淆理論的成熟,經過混淆的Java程式碼還是能夠很好地防止[反編譯。下面我們會詳細介紹混淆技術,因為混淆是一種保護Java程式的重要技術。
混淆技術介紹
到目前為止,對於Java程式的保護,混淆技術還是最基本的保護方法。Java混淆工具也非常多,包括商業的、免費的、開放原始碼的。Sun公司也提供了自己的混淆工具。它們大多都是對Class檔案進行混淆處理,也有少量工具首先對原始碼進行處理,然後再對Class進行處理,這樣加大了混淆處理的力度。
目前,商業上比較成功的混淆工具包括JProof公司的1stBarrier系列、Eastridge公司的JShrink和 4thpass.com的SourceGuard等。主要的混淆技術按照混淆目標可以進行如下分類,它們分別為符號混淆(Lexical Obfuscation)、資料混淆(Data Obfuscation)、控制混淆(Control Obfuscation)、預防性混淆(Prevent Transformation)。
符號混淆
在Class中存在許多與程式執行本身無關的資訊,例如方法名稱、變數名稱,這些符號的名稱往往帶有一定的含義。例如某個方法名為 getKeyLength(),那麼這個方法很可能就是用來返回Key的長度。
符號混淆就是將這些資訊打亂,把這些資訊變成無任何意義的表示,例如將所有的變數從vairant_001開始編號;對於所有的方法從method_001開始編號。
這將對反編譯帶來一定的困難。對於私有函式、區域性變數,通常可以改變它們的符號,而不影響程式的執行。但是對於一些介面名稱、公有函式、成員變數,如果有其它外部模組需要引用這些符號,我們往往需要保留這些名稱,否則外部模組找不到這些名稱的方法和變數。因此,多數的混淆工具對於符號混淆,都提供了豐富的選項,讓使用者選擇是否、如何進行符號混淆。
資料混淆
資料混淆是對程式使用的資料進行混淆。混淆的方法也有多種,主要可以分為改變資料儲存及編碼(Store and Encode Transform)、改變資料訪問(Access Transform)。
改變資料儲存和編碼可以打亂程式使用的資料儲存方式。例如將一個有10個成員的陣列,拆開為10個變數,並且打亂這些變數的名字;將一個兩維陣列轉化為一個一維陣列等。對於一些複雜的資料結構,我們將打亂它的資料結構,例如用多個類代替一個複雜的類等。
另外一種方式是改變資料訪問。例如訪問陣列的下標時,我們可以進行一定的計算,圖5就是一個例子。
在實踐混淆處理中,這兩種方法通常是綜合使用的,在打亂資料儲存的同時,也打亂資料訪問的方式。經過對資料混淆,程式的語義變得複雜了,這樣增大了反編譯的難度。
控制混淆
控制混淆就是對程式的控制流進行混淆,使得程式的控制流更加難以[反編譯,通常控制流的改變需要增加一些額外的計算和控制流,因此在效能上會給程式帶來一定的負面影響。有時,需要在程式的效能和混淆程度之間進行權衡。控制混淆的技術最為複雜,技巧也最多。這些技術可以分為如下幾類:
增加混淆控制通過增加額外的、複雜的控制流,可以將程式原來的語義隱藏起來。例如,對於按次序執行的兩個語句A、B,我們可以增加一個控制條件,以決定B的執行。通過這種方式加大反彙編的難度。但是所有的干擾控制都不應該影響B的執行。圖6就給出三種方式,為這個例子增加混淆控制。
控制流重組重組控制流也是重要的混淆方法。例如,程式呼叫一個方法,在混淆後,可以將該方法程式碼嵌入到呼叫程式當中。反過來,程式中的一段程式碼也可以轉變為一個函式呼叫。另外,對於一個迴圈的控制流,為可以拆分多個迴圈的控制流,或者將迴圈轉化成一個遞迴過程。這種方法最為複雜,研究的人員也非常多。
預防性混淆
這種混淆通常是針對一些專用的反編譯器而設計的,一般來說,這些技術利用反編譯器的弱點或者Bug來設計混淆方案。例如,有些反編譯器對於 Return後面的指令不進行反編譯,而有些混淆方案恰恰將程式碼放在Return語句後面。這種混淆的有效性對於不同反編譯器的作用也不太相同的。一個好的混淆工具,通常會綜合使用這些混淆技術。