隨筆-3  評論-0  文章-0  trackbacks-0

          一、 混淆器

            目前,開發人員使用的比較多的保護代碼的方法是用混淆器。混淆器是采用一些方法將類,變量,方法,包的名字改為無意義的字符串;使用非法的字符代替符號;貼加一些代碼使反編譯軟件崩潰;貼加一些無關的指令或永遠執行不到的指令等使反編譯無法成功或所得的代碼可讀性很差。這樣就實現了反反編譯的目的。我們來做個演示。原始代碼如下:

           

          import java.io.*;

          import java.security.*;

          public class sKey_kb{

          public static void main(String args[]) throws Exception{

          FileInputStream f=new FileInputStream("key1.dat");

          ObjectInputStream b=new ObjectInputStream(f);

          Key k=(Key)b.readObject();

          byte[] kb=k.getEncoded();

          FileOutputStream f2=new FileOutputStream("keykb1.dat");

          f2.write(kb);

          for(int i=0;i
          System.out.print(kb[i]+",");

          } } }

           

            使用混淆器后,再用jad反編譯得代碼如下:

          import java.io.*;

          import java.security.Key;

          public class sKey_kb{

          public skey() {}

          public static void main(String args[]) {

          FileInputStream fileinputstream=new FileInputStream(ma);

          ObjectInputStream objectinputstream=new ObjectInputStream(fileinputstream);

          Key key=(Key)b.readObject();

          byte abyte0[]=key.getEncoded();

          FileOutputStream fileoutputstream=new FileOutputStream(na);

          fileoutputstream.write(abyte0);

          for(int i=0;i
          System.out.print(abyte0[i]+oa);

          }

          private static String a(String s){

          int i=s.length();

          char ac[]=new char[i];

          for(int j=0;j
          return new String(ac);

          }

          private static String ma="u5AA1u5AAFu5AF3u5AFBu5AE4u5AAEu5AABu5ABE";

          private static String na="u5AA1u5AAFu5AB3u5AA1u5AA8u5AFBu5AE4u5AAEu5AABu5ABE";

          private static String oa="u5AE6";

          public static{

          ma=a(ma);

          na=a(ma)

          oa=a(oa);

          } }


           混淆后,再反編譯所仍然能得到源代碼,但顯然,所得代碼與原始代碼比,變得難以讀懂,代碼中多了其他的方法,文件名等信息也被打亂了。并且,把以上代碼寫進sKey_kb.java中,無法通過編譯。

            但是,如果在編寫軟件時,在軟件中寫入某些注冊信息,或一些簡單的算法,通過反編譯,還是有可能得到這些信息的,從而未能達到保護軟件的目的。反編譯器與混淆器之間的斗爭是永無止盡的。所以從其他角度去保護java的源代碼是很有必要。


          二、 網絡加載重要類

            在java中提供了一個ClassLoader類,這個類可以讓我們使用類加載器將所需要的java字節碼文件加載到jvm中。我們通過重寫這個類,可以實現從網絡通過url加載java字節碼文件。這樣,我們就可以把一些重要的,隱秘的class放在網絡服務器上,通過口令去檢驗是否有權限下載該類。從而實現java代碼保護的目的。其次在java中正好提供了URLClassLoader這個類,通過此類,正好可以實現我們的目的。URLClassLoader類的基本使用方法是通過一個URL類型的數組告訴URLClassLoader類的對象是從什么地方加載類,然后使用loadclass()方法,從給定的URL中加載字節碼文件,獲得它的方法,然后再執行。

            具體步驟如下:

            1.創建URL

          URL url[]={

          new URL("file:///c:/classloader/web"),

          new URL("

          };

           

            2.創建URLClassLoader對象

          URLClassLoader cl=new URLClassLoader(url);

           

            3.使用URLClassLoader對象加載字節碼文件

          Class class=cl.loadClass("class1");

           

            4.執行靜態方法

          Class getarg[]={

          (new String [1]).getClass() };

          Method m=class.getMethod("main",getarg);

          String[] myl={"arg1 passed","arg2 passed");

          Object myarg[]={myl};

          m.invole(null,myarg);

           


          三、 加密重要類

            使用網絡加載重要類的方法固然有一定的用處,但是,在遇到無網絡的情況時,還是無法解決我們的問題。對于這種情況,我們只能把所有文件放在本地計算機上。那么,對此我們該怎么做才能保護好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;

          //步驟1:如果類已經在系統緩沖之中,我們就不需要再次裝入它

          clasz = findLoadedClass( name );

          if (clasz != null)

          return clasz;

          byte classData[] = /* 通過某種方法獲取字節碼數據 */;

          if (classData != null) {

          clasz = defineClass( name, classData, 0, classData.length );

          }

          //步驟2:如果上面沒有成功,

          if (clasz == null)

          clasz = findSystemClass( name );

          //步驟3:如有必要,則裝入相關的類

          if (resolve && clasz != null)

          resolveClass( clasz );

          return clasz;

          } catch( IOException ie ) {

          throw new ClassNotFoundException( ie.toString() );

          } catch( GeneralSecurityException gse ) {

          throw new ClassNotFoundException( gse.toString() );

          } }

           

            代碼中的大部分對所有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 {

          this.key = key;

          String algorithm = "DES";

          SecureRandom sr = new SecureRandom();

          System.err.println( "[DecryptStart: creating cipher]" );

          cipher = Cipher.getInstance( algorithm );

          cipher.init( Cipher.DECRYPT_MODE, key, sr );

          }

          // main過程:我們要在這里讀入密匙,創建DecryptStart的

          static public void main( String args[] ) throws Exception {

          String keyFilename = args[0];

          String appName = args[1];

          String realArgs[] = new String[args.length-2];

          System.arraycopy( args, 2, realArgs, 0, args.length-2 );

          System.err.println( "[DecryptStart: reading key]" );

          byte rawKey[] = Util.readFile( keyFilename );

          DESKeySpec dks = new DESKeySpec( rawKey );

          SecretKeyFactory keyFactory = SecretKeyFactory.getInstance( "DES" );

          SecretKey key = keyFactory.generateSecret( dks );

          DecryptStart dr = new DecryptStart( key );

          System.err.println( "[DecryptStart: loading "+appName+"]" );

          Class clasz = dr.loadClass( appName );

          String proto[] = new String[1];

          Class mainArgs[] = { (new String[1]).getClass() };

          Method main = clasz.getMethod( "main", mainArgs );

          Object argsArray[] = { realArgs };

          System.err.println( "[DecryptStart: running "+appName+".main()]" );

          main.invoke( null, argsArray );

          }

           

            雖然應用本身經過了加密,但啟動程序DecryptStart沒有加密。攻擊者可以反編譯啟動程序并修改它,把解密后的類文件保存到磁盤。降低這種風險的辦法之一是對啟動程序進行高質量的模糊處理。或者,啟動程序也可以采用直接編譯成機器語言的代碼,使得啟動程序具有傳統執行文件格式的安全性.比如使用java的jini技術,來實現解密部分,就可以作到。當然,這是需要付出一定的代價的,就是喪失了java的最大特點--平臺無關性。不過,jni技術可以用c語言在多種平臺實現,我們可以在不同的平臺編寫不同的啟動程序。


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 衢州市| 屏东县| 政和县| 拉萨市| 花莲市| 宁都县| 邮箱| 灵川县| 土默特右旗| 任丘市| 谷城县| 子洲县| 彰化市| 灯塔市| 商河县| 清丰县| 汝南县| 西昌市| 河南省| 洪湖市| 霍山县| 阳曲县| 谢通门县| 城固县| 玉龙| 哈巴河县| 定结县| 上犹县| 囊谦县| 桐梓县| 肇源县| 玛多县| 宝山区| 康定县| 秀山| 辽源市| 上饶县| 叙永县| 微博| 兰溪市| 利川市|