由於Java位元組碼的抽象級別較高,因此它們較容易被反編譯。下面介紹了幾種常用的方法,用於保護Java位元組碼不被反編譯。通常,這些方法不能夠絕對防止程式被反編譯,而是加大反編譯的難度而已,因為這些方法都有自己的使用環境和弱點。
1.隔離Java程式
最簡單的方法就是讓使用者不能夠訪問到Java Class程式,這種方法是最根本的方法,具體實現有多種方式。例如,開發人員可以將關鍵的Java Class放在伺服器端,客戶端透過訪問伺服器的相關介面來獲得服務,而不是直接訪問Class檔案。這樣駭客就沒有辦法反編譯Class檔案。目前,透過介面提供服務的標準和協議也越來越多,例如 HTTP、Web Service、RPC等。但是有很多應用都不適合這種保護方式,例如對於單機執行的程式就無法隔離Java程式。
2.對Class檔案進行加密
為了防止Class檔案被直接反編譯,許多開發人員將一些關鍵的Class檔案進行加密,例如對註冊碼、序列號管理相關的類等。在使用這些被加密的類之前,程式首先需要對這些類進行解密,而後再將這些類裝載到JVM當中。這些類的解密可以由硬體完成,也可以使用軟體完成。
在實現時,開發人員往往透過自定義ClassLoader類來完成加密類的裝載(注意由於安全性的原因,Applet不能夠支援自定義的ClassLoader)。自定義的ClassLoader首先找到加密的類,而後進行解密,最後將解密後的類裝載到JVM當中。在這種保護方式中,自定義的ClassLoader是非常關鍵的類。由於它本身不是被加密的,因此它可能成為駭客最先攻擊的目標。如果相關的解密金鑰和演算法被攻克,那麼被加密的類也很容易被解密。
3.轉換成原生代碼
將程式轉換成原生代碼也是一種防止反編譯的有效方法。因為原生代碼往往難以被反編譯。開發人員可以選擇將整個應用程式轉換成原生代碼,也可以選擇關鍵模組轉換。如果僅僅轉換關鍵部分模組,Java程式在使用這些模組時,需要使用JNI技術進行呼叫。當然,在使用這種技術保護Java程式的同時,也犧牲了Java的跨平臺特性。對於不同的平臺,我們需要維護不同版本的原生代碼,這將加重軟體支援和維護的工作。不過對於一些關鍵的模組,有時這種方案往往是必要的。為了保證這些原生代碼不被修改和替代,通常需要對這些程式碼進行數字簽名。在使用這些原生代碼之前,往往需要對這些原生代碼進行認證,確保這些程式碼沒有被駭客更改。如果簽名檢查透過,則呼叫相關JNI方法。
4.程式碼混淆
程式碼混淆是對Class檔案進行重新組織和處理,使得處理後的程式碼與處理前程式碼完成相同的功能(語義)。但是混淆後的程式碼很難被反編譯,即反編譯後得出的程式碼是非常難懂、晦澀的,因此反編譯人員很難得出程式的真正語義。從理論上來說,駭客如果有足夠的時間,被混淆的程式碼仍然可能被破解,甚至目前有些人正在研製反混淆的工具。但是從實際情況來看,由於混淆技術的多元化發展,混淆理論的成熟,經過混淆的Java程式碼還是能夠很好地防止反編譯。下面我們會詳細介紹混淆技術,因為混淆是一種保護Java程式的重要技術。
由於Java位元組碼的抽象級別較高,因此它們較容易被反編譯。下面介紹了幾種常用的方法,用於保護Java位元組碼不被反編譯。通常,這些方法不能夠絕對防止程式被反編譯,而是加大反編譯的難度而已,因為這些方法都有自己的使用環境和弱點。
1.隔離Java程式
最簡單的方法就是讓使用者不能夠訪問到Java Class程式,這種方法是最根本的方法,具體實現有多種方式。例如,開發人員可以將關鍵的Java Class放在伺服器端,客戶端透過訪問伺服器的相關介面來獲得服務,而不是直接訪問Class檔案。這樣駭客就沒有辦法反編譯Class檔案。目前,透過介面提供服務的標準和協議也越來越多,例如 HTTP、Web Service、RPC等。但是有很多應用都不適合這種保護方式,例如對於單機執行的程式就無法隔離Java程式。
2.對Class檔案進行加密
為了防止Class檔案被直接反編譯,許多開發人員將一些關鍵的Class檔案進行加密,例如對註冊碼、序列號管理相關的類等。在使用這些被加密的類之前,程式首先需要對這些類進行解密,而後再將這些類裝載到JVM當中。這些類的解密可以由硬體完成,也可以使用軟體完成。
在實現時,開發人員往往透過自定義ClassLoader類來完成加密類的裝載(注意由於安全性的原因,Applet不能夠支援自定義的ClassLoader)。自定義的ClassLoader首先找到加密的類,而後進行解密,最後將解密後的類裝載到JVM當中。在這種保護方式中,自定義的ClassLoader是非常關鍵的類。由於它本身不是被加密的,因此它可能成為駭客最先攻擊的目標。如果相關的解密金鑰和演算法被攻克,那麼被加密的類也很容易被解密。
3.轉換成原生代碼
將程式轉換成原生代碼也是一種防止反編譯的有效方法。因為原生代碼往往難以被反編譯。開發人員可以選擇將整個應用程式轉換成原生代碼,也可以選擇關鍵模組轉換。如果僅僅轉換關鍵部分模組,Java程式在使用這些模組時,需要使用JNI技術進行呼叫。當然,在使用這種技術保護Java程式的同時,也犧牲了Java的跨平臺特性。對於不同的平臺,我們需要維護不同版本的原生代碼,這將加重軟體支援和維護的工作。不過對於一些關鍵的模組,有時這種方案往往是必要的。為了保證這些原生代碼不被修改和替代,通常需要對這些程式碼進行數字簽名。在使用這些原生代碼之前,往往需要對這些原生代碼進行認證,確保這些程式碼沒有被駭客更改。如果簽名檢查透過,則呼叫相關JNI方法。
4.程式碼混淆
程式碼混淆是對Class檔案進行重新組織和處理,使得處理後的程式碼與處理前程式碼完成相同的功能(語義)。但是混淆後的程式碼很難被反編譯,即反編譯後得出的程式碼是非常難懂、晦澀的,因此反編譯人員很難得出程式的真正語義。從理論上來說,駭客如果有足夠的時間,被混淆的程式碼仍然可能被破解,甚至目前有些人正在研製反混淆的工具。但是從實際情況來看,由於混淆技術的多元化發展,混淆理論的成熟,經過混淆的Java程式碼還是能夠很好地防止反編譯。下面我們會詳細介紹混淆技術,因為混淆是一種保護Java程式的重要技術。