Java誕生於年是一門較年輕的語言它以平台無關性安全性面向對象分布式鍵壯性等特點贏得了眾多程序員的青睐特別是它簡潔的面向對象的語言風格更讓許多人對它愛不釋手但人們在使用Java的過程中會發現它有幾個致命的弱點運行速度慢用戶使用不便源代碼保護機制不夠安全特別是在保護源代碼方面Java是基於解釋一種叫Java字節碼的中間代碼來運行其程序的而且Jvm比計算機的微處理器要簡單的多文檔也很齊全結果造成其目標程序很容易被反編譯而且所得代碼和其原始代碼十分相似甚至可以一模一樣可讀性相當好這就給Java的代碼保護帶來了不利但要實現Java程序的保護也不是不可能的經研究和總結至少有三種實現方式混淆器網絡加載重要類加密重要類
混淆器
目前開發人員使用的比較多的保護代碼的方法是用混淆器混淆器是采用一些方法將類變量方法包的名字改為無意義的字符串使用非法的字符代替符號貼加一些代碼使反編譯軟件崩潰貼加一些無關的指令或永遠執行不到的指令等使反編譯無法成功或所得的代碼可讀性很差這樣就實現了反反編譯的目的我們來做個演示原始代碼如下
import Javaio*
import Javasecurity*
public class sKey_kb{
public static void main(String args[]) throws Exception{
FileInputStream f=new FileInputStream(keydat)
ObJectInputStream b=new ObJectInputStream(f)
Key k=(Key)breadObJect()
byte[] kb=kgetEncoded()
FileOutputStream f=new FileOutputStream(keykbdat)
fwrite(kb)
for(int i=i
Systemoutprint(kb[i]+)
} } }
使用混淆器後再用Jad反編譯得代碼如下
import Javaio*
import JavasecurityKey
public class sKey_kb{
public skey() {}
public static void main(String args[]) {
FileInputStream fileinputstream=new FileInputStream(ma)
ObJectInputStream obJectinputstream=new ObJectInputStream(filein
putstream)
Key key=(Key)breadObJect()
byte abyte[]=keygetEncoded()
FileOutputStream fileoutputstream=new FileOutputStream(na)
fileoutputstreamwrite(abyte)
for(int i=i
Systemoutprint(abyte[i]+oa)
}
private static String a(String s){
int i=slength()
char ac[]=new char[i]
for(int J=J
return new String(ac)
}
private static String ma=uAAuAAFuAFuAFBuAEuAAEuAABuABE
private static String na=
uAAuAAFuABuAAuAAuAFBuAEuAAEuAABuABE
private static String oa=uAE
public static{
ma=a(ma)
na=a(ma)
oa=a(oa)
} }
混淆後再反編譯所仍然能得到源代碼但顯然所得代碼與原始代碼比變得難以讀懂代碼中多了其他的方法文件名等信息也被打亂了並且把以上代碼寫進sKey_kbJava中無法通過編譯
但是如果在編寫軟件時在軟件中寫入某些注冊信息或一些簡單的算法通過反編譯還是有可能得到這些信息的從而未能達到保護軟件的目的反編譯器與混淆器之間的斗爭是永無止盡的所以從其他角度去保護Java的源代碼是很有必要
網絡加載重要類
在Java中提供了一個ClassLoader類這個類可以讓我們使用類加載器將所需要的Java字節碼文件加載到Jvm中我們通過重寫這個類可以實現從網絡通過url加載Java字節碼文件這樣我們就可以把一些重要的隱秘的class放在網絡服務器上通過口令去檢驗是否有權限下載該類從而實現Java代碼保護的目的其次在Java中正好提供了URLClassLoader這個類通過此類正好可以實現我們的目的URLClassLoader類的基本使用方法是通過一個URL類型的數組告訴URLClassLoader類的對象是從什麼地方加載類然後使用loadclass()方法從給定的URL中加載字節碼文件獲得它的方法然後再執行
具體步驟如下
創建URL
URL url[]={
new URL(file:///c/classloader/web)
new URL(/Javaclass/)
}
創建URLClassLoader對象
URLClassLoader cl=new URLClassLoader(url)
使用URLClassLoader對象加載字節碼文件
Class class=clloadClass(class)
執行靜態方法
Class getarg[]={
(new String [])getClass() }
Method m=classgetMethod(maingetarg)
String[] myl={arg passedarg passed)
ObJect myarg[]={myl}
minvole(nullmyarg)
加密重要類
使用網絡加載重要類的方法固然有一定的用處但是在遇到無網絡的情況時還是無法解決我們的問題對於這種情況我們只能把所有文件放在本地計算機上那麼對此我們該怎麼做才能保護好Java代碼呢?
其實要實現這一點並不難只需要對一些重要的類實行加密就可以了當然在裝載時加密的類是需要解密才能被ClassLoader識別的所以我們必須自己創建ClassLoader類在標准Java api中ClassLoader有幾個重要的方法創建定制ClassLoader時我們只需覆蓋其中的一個即loadClass添加獲取原始類文件數據的代碼這個方法有兩個參數類的名字以及一個表示JVM是否要求解析類名字的標記(即是否同時裝入有依賴關系的類)如果這個標記為true我們只需在返回JVM之前調用resolveClass
原代碼如下
public Class loadClass( String name boolean resolve )
throws ClassNotFoundException {
try {
Class clasz = null
//步驟如果類已經在系統緩沖之中我們就不需要再次裝入它
clasz = findLoadedClass( name )
if (clasz != null)
return clasz
byte classData[] = /* 通過某種方法獲取字節碼數據 */
if (classData != null) {
clasz = defineClass( name classData classDatalength )
}
//步驟如果上面沒有成功
if (clasz == null)
clasz = findSystemClass( name )
//步驟如有必要則裝入相關的類
if (resolve && clasz != null)
resolveClass( clasz )
return clasz
} catch( IOException ie ) {
throw new ClassNotFoundException( ietoString() )
} catch( GeneralSecurityException gse ) {
throw new ClassNotFoundException( gsetoString() )
} }
代碼中的大部分對所有ClassLoader對象來說都一樣但有一小部分是特有的在處理過程中ClassLoader對象要用到其他幾個輔助方法findLoadedClass用來進行檢查以便確認被請求的類當前是否存在loadClass方法應該首先調用它defineClass獲得原始類文件字節碼數據之後調用defineClass把它轉換成對象任何loadClass實現都必須調用這個方法findSystemClass提供默認ClassLoader的支持如果用來尋找類的定制方法不能找到指定的類則可以調用該方法嘗試默認的裝入方式resolveClass當JVM想要裝入的不僅包括指定的類而且還包括該類引用的所有其他類時它會把loadClass的resolve參數設置成true這時我們必須在返回剛剛裝入的Class對象給調用者之前調用resolveClass
接下來就是加密解密部分Java加密擴展即Java Cryptography Extension簡稱JCE是Sun的加密服務軟件包含了加密和密匙生成功能我們可以用DES算法加密和解密字節碼用JCE加密和解密數據是要遵循一些基本步驟的(可以參考<>這裡就不祥述了)
加密完成後就是通過解密來獲取原始類的Java字節碼可以通過一個DecryptStart程序運行經過加密的應用
具體方法如下
public class DecryptStart extends ClassLoader
{
private SecretKey key
private Cipher cipher
public DecryptStart( SecretKey key ) throws GeneralSecurityException
IOException {
thiskey = key
String algorithm = DES
SecureRandom sr = new SecureRandom()
Systemerrprintln( [DecryptStart creating cipher] )
cipher = CiphergetInstance( algorithm )
cipherinit( CipherDECRYPT_MODE key sr )
}
// main過程我們要在這裡讀入密匙創建DecryptStart的
static public void main( String args[] ) throws Exception {
String keyFilename = args[]
String appName = args[]
String realArgs[] = new String[argslength]
Systemarraycopy( args realArgs argslength )
Systemerrprintln( [DecryptStart reading key] )
byte rawKey[] = UtilreadFile( keyFilename )
DESKeySpec dks = new DESKeySpec( rawKey )
SecretKeyFactory keyFactory = SecretKeyFactorygetInstance( DES )
SecretKey key = keyFactorygenerateSecret( dks )
DecryptStart dr = new DecryptStart( key )
Systemerrprintln( [DecryptStart loading +appName+] )
Class clasz = drloadClass( appName )
String proto[] = new String[]
Class mainArgs[] = { (new String[])getClass() }
Method main = claszgetMethod( main mainArgs )
ObJect argsArray[] = { realArgs }
Systemerrprintln( [DecryptStart running +appName+main()] )
maininvoke( null argsArray )
}
雖然應用本身經過了加密但啟動程序DecryptStart沒有加密攻擊者可以反編譯啟動程序並修改它把解密後的類文件保存到磁盤降低這種風險的辦法之一是對啟動程序進行高質量的模糊處理或者啟動程序也可以采用直接編譯成機器語言的代碼使得啟動程序具有傳統執行文件格式的安全性比如使用Java的Jini技術來實現解密部分就可以作到當然這是需要付出一定的代價的就是喪失了Java的最大特點——平台無關性不過Jni技術可以用c語言在多種平台實現我們可以在不同的平台編寫不同的啟動程序
綜合實例
對於一些需要網絡支持的軟件來說可以建立一個Web站點在站點上存放該軟件的關鍵類並且建立用戶管理機制用戶直接登陸網站進行確認是許可用戶則發放解密key文件讓其下載關鍵類在本地解密運行這樣作的優點是建立的Web站點可以有效的管理密鑰以及用戶資料從而起到加強保護軟件源代碼的作用並方便軟件升級用C/S結構是不錯的選擇
From:http://tw.wingwit.com/Article/program/Java/hx/201311/25534.html