創建並使用自定義的類加載器實現代碼混淆器

 創建並使用自定義的類加載器

JVM中除了根加載器之外的所有類加載器都是ClassLoader子類的實例,開發者可以通過擴展ClassLoader的子類,並重寫該ClassLoader所包含的方法來實現自定義的類加載器

ClassLoader類有如下三個關鍵方法:

loaderClass(String name,Boolean resolve):該方法爲ClassLoader的入口點,根據指定的二進制名稱來加載類,系統就是調用ClassLoader的該方法來獲取指定類對應的Class對象

findClass(String name):根據二進制名稱來查找類
如果需要實現自定義的ClassLoader,可以通過重寫以上兩個方法來實現,推薦重寫findClass()方法,而不是重寫loadClass()方法,因爲loadClass()方法的執行步驟如下:

1.       用findLoadedClass(String )來檢查是否已經加載類,如果已經加載則直接返回。

2.       在父類加載器上調用loadClass方法,如果父類加載器爲null,則使用根類加載器來加載

3.       調用findClass(String)方法查找類

從上面方法可以看出,重寫findClass()方法可以避免覆蓋默認類加載器的父類委託,緩衝機制兩種策略,如果重寫loadClass()方法,則實現邏輯更爲複雜

ClassLoader類還有一個核心方法:Class defineClass(String name,byte[] b,int off,int len)該方法負責將指定類的字節碼文件(即Class文件)讀入字節數組:byte[]b,並把它轉化爲Class對象,該字節碼文件可以來源於文件,網絡等。

 findSystemClass(String name)從本地文件系統中裝入文件,在本地文件中尋找類文件,如果存在,就用defineClass將原始字節轉化成Class對象,以將該文件轉化成類

static getSystemClassloader():這是一個靜態方法,用於返回系統類加載器

getParent():獲取該類加載器的父類加載器

resolveClass(Class<?> c):鏈接指定的類,類加載器可以使用此方法來鏈接類c.

findLoadedClass(String name):如果此Java虛擬機已經裝在了名爲name的類,則直接返回該類對應的Class實例,否則,返回null,該方法是java類加載裏緩存機制的體現

 

 

自定義類加載器:

Public class CompileClassLoader extends ClassLoader

{

   //讀取一個文件的內容

   Private byte[] getBytes(String filename) throws IOException

{

  File file = new File(filename);

  Long len = file.length();

  Byte[] raw = new byte[(int)len];

 FileInputStream fin = new FileInuptStream(file);

 //一次讀取class文件的全部二進制數據

Int r = fin.read(raw);

If(r!=len)

Throw new IOException(“無法讀取全部文件”+r+”!=”+len);

Fin.close();

Return raw;

}

//定義編譯指定java文件的方法

Private Boolean compile(String javaFile) throws IOException

{

 System.out.println(“CompileClassLoader:正在編譯”+javaFile+”……..”);

 //調用系統的javac命令

Process p = Runtime.getRuntime().exec(“javac”+javaFile);

Try{

      //其它線程都等待這個線程完成

      p.waitFor();

}catch(InterruptedException ie)

{

 System.out.println(ie);

}

//獲取javac 的線程的退出值

Int ret =p.exitValue();

//返回編譯是否成功

Return  ret==0;

}

//重寫Classloader的findCLass方法

Protected Class<?> findClass(String name)throws ClassNotFoundException

{

   Class clazz = null;

   //將包路徑中的.替換成斜線/

String fileStub = name.replace(“.”,”/”);

String javaFilename = fileStub+”.java”;

String classFilename = fileStub+”.class”;

File javaFile= new File(javaFilename);

File classFile = new File(classFilename);

//當指定Java源文件存在,且class文件不存在,或者Java源文件的修改時間比class文件//修改時間晚時,重新編譯

   If(javaFile.exists()&&(!classFIle.exists())||javaFIle.lastModified()>classFile.lastModified())

{

 

     Try{

  //如果編譯失敗,或該Class文件不存在

  If(!compile(javaFilename)||!classFile.exists())

{

 Throw new ClassNotFoundException(“ClassNotFoundException:”+javaFilename);

}

}catch(IOException ex)

  {

     Ex.printStackTrace();

}

}           

//如果class文件存在,系統負責將該文件轉化成class對象

If(classFile.exists())

{

  Try{

     //將class文件的二進制數據讀入數組

    Byte[] raw= getBytes(classFilename);

   //調用Classloader的defineClass方法將二進制數據轉換成class對象

   Clazz = defineClass(name,raw,0.raw.length);

}catch(IOException ie)

{

  Ie.printStackTrace();

}

}

//如果claszz爲null,表明加載失敗,則拋出異常

If(clazz==null){

   Thow new ClassNotFoundException(name);

}

Return clazz;

}

 

//定義一個主方法

Public static void main(String [] args )throws Exception

{

//如果運行該程序時沒有參數,即沒有目標類

  If(args.length<1){

      System.out.println(“缺少運行的目標類,請按如下格式運行java源文件:”);

      System.out.println(“java CompileClassLoader ClassName”);

}

//第一個參數是需要運行的類

String progClass = args[0];

//剩下的參數將作爲運行目標類時的參數,所以將這些參數複製到一個新數組中

String progargs[] = new String [args.leng-1];

Sytem.arraycopy(args,1,progargs,0,progArgs.length);

CompileClassLoader cl = new CompileClassLoader();

//加載需要運行的類

Class<?> clazz = cl.loadClass(progClass);

//獲取需要運行的類的主方法

Method main= clazz.getMethod(“main”,(new String[0]).getClass());

Object argsArray[] = {progargs};

Main.invoke(null,argsArray);

}

}

自定義加載器實現如下功能:

執行代碼前自動驗證數字簽名

根據用戶提供的密碼解密代碼,從而可以實現代碼混淆器來避免反編譯class文件

根據用戶需求來動態的加載類

根據應用需求把其他數據以字節碼的形式加載到應用中

URLClassLoader類

Java爲ClassLoader提供了一個URLClassLoader實現類,該類也是系統類加載器和擴展類加載器的父類(此處是父類,而不是父類加載器,這裏是類與類之間的繼承關係),URLClassLoader功能比較強大,既可以從本地文件系統中獲取二進制文件來加載類,也可以從遠程主機上獲取二進制文件來加載類

URLClassLoader(URL[] urls):使用默認的父類加載器創建一個ClassLoader對象,該對象將從urls所指定的系列路徑來查詢並加載類

URLClassLoader(URL[] urls,ClassLoader parent):使用指定的父類加載器創建一個Classloader對象,其它功能與前一個構造器相同

一旦得到URLClassloader對象之後,就可以調用該對象的loadClass方法來加載指定的類

下列程序示範瞭如何直接從文件系統中加載Mysql驅動,並使用該驅動獲取數據庫連接,通過這種方式取得數據庫連接就可以無需將mysql驅動添加到CLASSPATH環境變量中

Public class URLClassLoaderTest

{

  Private static Connection conn;

  //定義一個獲取數據庫連接方法

  Public static Connection getConn(String url,String user,String pass) throws Exception

  {

If(conn == null){

  //創建一個URL數組

URL[]  urls = {new URL(“file:mysql-connector-java-3.1.10-bin.jar”)};

//以默認的ClassLoader作爲父ClassLoader,創建URLClassLoader

URLClassLoader myClassLoader= new URLClassLoader(urls);

//加載mysql的JDBC驅動,並創建默認實例

Driver driver = (Driver)myClassLoader.loadClass(“com.mysql.jdbc.Driver”).newInstance();

//創建一個設計JDBC連接屬性的Properties對象

Properties props = new Properties();

//至少需要爲該對象傳入user和password兩個屬性

Props.setProperty(“user”,user);

Props.setProperty(“password”,pass);

//調用Driver 對象的connect方法來取得數據庫連接

Conn = driver.connect(“jdbc:mysql://localhost:3306/mysql”,props);

 

}

Return conn;

Public static void main(String[] args)throws Exception

{

  System.out.println(getConn(“jdbc:mysql///j2ee”,”root”,”root”));

}

}

 

}

上面程序創建了URLCLassLoader對象,該對象使用默認的父類加載器,該類加載器的類加載路徑是當前路徑下的mysql-connector-java-3.1.10-bin.jar文件,我們將mysql的驅動複製到該路徑下,這樣保證該ClassLoader可以正常加載到com.mysql.jdbc.Driver類

使用ClassLoader的loadClass加載指定類,並調用Class對象的newInstance()方法來創建一個該類的實例----也就是得到com,mysql.jdbc.Driver類的對象,當然該對象的實現類實現類java.sql.Driver接口,所以程序將其強制類型轉化爲Driver,最後程序通過Driver而不是DriverManager來獲取數據庫連接,當我們創建URLClassLoader時傳入了一個URL數組參數,則該ClassLoader就可以從這系列URL指定的資源中加載指定的類,這裏的URL可以以file:爲前綴,表明從本地文件系統加載,可以以http:爲前綴,表明從互聯網通過http訪問來加載,也可以以ftp:爲前綴,表明從互聯網通過FTP訪問來加載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章