JAVA 類文件保護分析論文

時間:2022-10-11 10:30:00

導語:JAVA 類文件保護分析論文一文來源于網友上傳,不代表本站觀點,若需要原創文章可咨詢客服老師,歡迎參考。

JAVA 類文件保護分析論文

【摘要】:由于Java語言面向對象和編譯成中間代碼執行的特點,其在抗反編譯和反盜版方面顯得尤其脆弱。本文針對Java軟件的特點,運用多種方法,綜合設計出一個保護Java類文件的方法。

關鍵詞:Class;加密;密鑰;代碼混淆

1.引言

目前,Java編程語言的應用在全世界范圍正流行,它廣泛的應用在Internet的數據庫、多媒體、CGI及動態網頁的制作方面。1999年在美國對Java程序員的需求量首次超過C++。經調查統計,Java語言應用在軟件領域占領著舉足輕重的地位,為人類科技文明進步奠定了重要基礎。然而,Java語言卻存在著巨大的安全隱患。Java是一種跨平臺的、解釋型語言。第一,Java源代碼編譯中間“字節碼”存儲于Class文件中。Class文件是一種字節碼形式的中間代碼,該字節碼中包括了很多源代碼的信息,例如變量名、方法名等;第二,由于跨平臺的需求,Java的指令集比較簡單通用,較容易得出程序的語義信息;第三,Java編譯器將每一個類編譯成一個單獨的文件,這也簡化了反編譯的工作;第四,Java的Class文件中,仍然保留所有的方法和變量的名稱,可以通過這些名稱來訪問變量和方法,這些符號往往帶有許多語義信息。因此,Java程序的這些特點,很容易對不經過處理的Java程序進行反編譯。目前,市場上有許多優秀的Java反編譯工具,能夠反編譯出非常接近源代碼的程序。所以,對開發人員來說,如何保護Java程序就變成一個非常重要的任務。

2.Java類文件的安全威脅

2.1Java的編譯

開發Java應用程序首先是使用編輯工具編寫Java的源代碼,然后使用編譯器編譯成虛擬機可執行的Class類文件。編譯后生成的類文件是一種有格式的中間代碼——字節碼文件,不能在本地機器上獨立運行,只能在Java虛擬機里解釋執行。Java編譯器不對變量和方法等符號的引用轉換為數值引用,也不確定程序執行過程中的內存布局,而是將這些符號的引用信息保留在類文件中,由解釋器在運行過程中創建內存布局,然后再通過查找表來確定一個變量或方法所在的地址[1]。

從Java類文件的結構及其實際數據可知Java類文件保留了源代碼文件的大部分信息,如所有的變量和方法等信息。正是由于這個特點,只要在各個平臺上實現了各自的Java虛擬機,不用修改Java應用程序的源代碼就可以在各個平臺上運行,真正做到跨平臺的特性,這也是Java能夠迅速流行起來的重要原因。

2.2Java的反編譯

反編譯是一個將目標代碼轉換成源代碼的過程[2]。而目標代碼是一種用語言表示的代碼,這種語言能通過實機或虛擬機直接執行。從本質上說,他需要根據小規模、低層次的行為來推斷大規模、高層次的行為。因此,反編譯目標代碼并不容易。

在JDK中,有一個反編譯器javap[3],利用該工具可以對Java類文件進行反編譯。經過

該工具反匯編后得到的結果并不是源代碼,但是使用javap進行反編譯的Java類文件可以得到成員變量、方法、行號以及局部變量名等信息[4]。在javap工具的基礎上,一些反編譯工具如Mocha,WinDis,DjDecompiler等工具可反編譯出和源代碼幾乎一摸一樣的代碼。

3.常用Java類文件保護方法

由于Java字節碼的抽象級別較高,容易被反編譯,所以就有了多種防止Java字節碼被反編譯的方法。

隔離Java程序:最簡單的方法就是讓用戶不能夠訪問到JavaClass程序,這種方法是最根本的方法,具體實現有多種方式。

代碼混淆:這種方法對Class文件進行重新組織和處理,使得處理前后的代碼具有相同的語義,被混淆后的代碼很難被反編譯。

轉換成本地代碼:本地代碼難以被反編譯,開發人員可以選擇將整個應用程序或關鍵模塊轉換成本地代碼。如果僅僅轉換關鍵模塊,在使用這些模塊時,需調用JNI技術,這將犧

牲Java的跨平臺特性

加密Class文件:為了防止Class文件被直接反編譯,可以將一些關鍵的Class文件加密

[5],例如對密鑰、加密算法、注冊碼、序列號管理相關的類等。在使用這些被加密的類之前先解密,然后再將其裝載到JVM中。

對比上述幾種方法,都存在其自身的優缺點。隔離Java程序只能適合網絡環境的客戶機/服務器結構或者分布式的環境,對單機運行的程序就無法隔離,而且Java程序需要使用安全機制保護服務器開放接口的使用,服務器的安全成了整個系統安全的焦點。代碼本地化,對于不同的平臺,需要維護不同版本的本地代碼,這將加重軟件支持和維護的工作。對Class文件進行加密,在使用時再進行解密,同時將關鍵加密代碼部分進行代碼混淆,這樣經過雙重處理后,代碼的安全性[6]提高了很多,該方法也是本文研究的重點。

4.Class文件加密技術

Java生成的Class文件大量暴露在客戶端,利用現在的反編譯工具可輕易的獲取源代碼,下面將講敘如何有效的保護Class文件。

第一,讀取本工程的所有待加密Class文件,并保存到byte型數組中;

publicstaticbyte[]symmetricEncrypt(byte[]key,byte[]classData){…};方法對讀取到的所

有Class文件進行加密,key為用來加密的密鑰,classData為所讀到的待加密Class文件,返回結果為加密后的Class文件,然后將其寫回原來的Class中,保證結構的完整性。

第二,加密過的Class文件在使用之前需先對其進行解密。

Java虛擬機有默認的類加載器,但是若要它根據用戶提供的密碼解密代碼就難以做到,此時需要通過自定義ClassLoader類來完成加密類的裝載。自定義的ClassLoader首先找到被加密過的類,然后進行解密,最后將解密后的類裝載到JVM中。這里我的自定義ClassLoader如下:

ClassLoaderappLoader=newEncryptedClassLoader(EncryptedClassLoader.class.getClassLoader(),newFile(args[1]));

Thread.currentThread().setContextClassLoader(appLoader);

finalClassapp=appLoader.loadClass(args[2]);

其中參數args[1]傳入的是方法所在的工程名,args[2]為主函數所在的類名。

在加載類后,系統的默認findClass()并未對加載的類作任何處理,由于Class文件已被

加密過,此時若運用系統方法findClass()則會拋出ClassNotFoundException()的異常,所以這里需要重構我自己的findClass()方法:

protectedClassfindClass(finalStringname)throwsClassNotFoundException{

finalStringclassResource=name.replace(''''.'''',''''/'''')+".class";finalURLclassURL=getResource(classResource);InputStreamin=null;

Filefile=newFile(classURL.getPath());

byte[]classBytes=newbyte[(int)file.length()];FileInputStreamfin=newFileInputStream(file);fin.read(classBytes);

……

classBytes=decrypt(classBytes);//解密

……

returndefineClass(name,classBytes,0,classBytes.length);

在這個函數中,我運用decrypt(classBytes);方法對所有的加密Class文件進行解密,并在其中調用方法publicstaticbyte[]symmetricEncrypto(byte[]key,byte[]byteSource){}將解密后的Class文件保存直原文件處,保持文件目錄級別不變,key為解密密鑰,byteSource為待

解密的byte型文件。至此,已完成對類文件的加密和解密,經過測試,功能已實現,Class

文件無法被反編譯。但為進一步加強程序的安全性,我做了如下的處理。

第三,對包含有關鍵信息的方法進行代碼混淆處理[7]。在上述內容中,方法symmetricEncrypt(byte[]key,byte[]classData)包含有加密所用到的算法,自定義的ClassLoader包含有關鍵信息,findClass(finalStringname)以及decrypt(classBytes);中包含有解密信息,由于它們本身不是被加密的,因此它可能成為黑客最先攻擊的目標。如果相關的解密密鑰和算法被攻克,那么被加密的類也很容易被解密。所以這里我對這些關鍵代碼進行代碼混淆。代碼混淆是對代碼進行重新組織和處理,使得處理后的代碼與處理前的代碼完成相同的功能,但是混淆后的代碼很難被反編譯。代碼混淆有符號混淆、數據混淆、控制混淆和預防性混淆。這里我采用數據混淆對關鍵代碼進行處理。

publicstaticbyte[]symmetricEncrypt(byte[]key,byte[]classData){…};處理如下:

//rawKey,byteSource為symmetricEncrypt(byte[],byte[])的待傳入參數

byte[]tempkey=null;

tempkey[0]=0x00;

for(inti=0;i<key.length;i++)

tempkey[i+1]=key[i];

tempkey[key.length+1]=0x11;

byte[]source=null;

source[0]=0x00;

for(inti=0;i<classData.length;i++)source[i+1]=classData[i];source[classData.length+1]=0x11;

publicstaticbyte[]symmetricEncrypt(byte[]tempkey,byte[]source){

//取tempkey和source的除第一個和最后一個byte的值

......

}

對publicClassloadClass(finalStringtempname,finalbooleanresolve){}方法進行處理:

Stringtempname="abcdefg"+name;//name:loadClass的第一個待傳入參數

publicClassloadClass(finalStringtempname,finalbooleanresolve){Stringname=tempname.substring(11,tempname.length());

......

}

對findClass(Stringname){}方法進行處理:

//name為findClass(Stringname)待傳入參數,先做如下處理

addname=name+"01357924680";

protectedClassfindClass(finalStringaddname){

name=addname.substring(0,addname.length()-11);

......//fingClass其他工作

}

intlen;//len=待傳文件file的長度:file.length()

byte[]classBytes=newbyte[(int)len];classBytes[len+1]=0x00;classBytes[len+2]=0x11;

//classBytes作為decrypt(byte[]classBytes)的傳入參數

privatestaticbyte[]decrypt(finalbyte[]classBytes){

byte[]data=newbyte[(int)classByte.lengt-2];

for(inti=0;i<data.length;i++)

data[i]=classBytes[i];

......//解密工作

}

5.結論

本文介紹了我針對Java類文件設計的保護方法,在眾多方法中,我選擇了對Class文件進行加密這一思想,加密之后又對包含重要信息的方法進行代碼混淆處理,這樣就對文件起到了雙重保護的作用。經過在Windows平臺上測試,效果良好,難以反編譯,起到了很好的保護作用。