生活随笔
收集整理的這篇文章主要介紹了
利用自定义的 ClassLoader 加密 Java Class 文件
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
本文演示利用自定義的 ClassLoader 加密?Java?Class 文件
首先,我們定義一個需要被加密的java?Class: classload.MyClassBase。 為了讓客戶端使用,需要定義一個?MyClassInterface, 這樣客戶端就不會直接引用?MyClassBase了,發布到客戶端的class文件中是不存在?MyClassBase這個類的。
MyClassBase定義:
[java]?view plaincopy
package?classload;????public?class?MyClassBase?implements?MyClassInterface?{??????public?void?say()?{??????????System.out.append("Hello?World!");??????}???}??
MyClassInteface 的定義:
[java]?view plaincopy
package?classload;??????public?interface?MyClassInterface?{??????public?void?say();??}??
我們把 classload/MyClassBase.class 這個文件進行加密處理, 變成另外一個文件,加密后的文件名可以放到任意位置, 這里我們把它放到 cipher/CipherMyClassBase.class。(CipherMyClassBase.class 就是加密后的文件)
如何加密后面再說, 先看看,客戶端是如何使用的:
[java]?view plaincopy
Class<?>?clz?=?loader.loadClass("classload.MyClassBase");??System.out.println("loaded?class:"?+?clz.getName()?+?"?by?"?+?clz.getClassLoader());??MyClassInterface?obj?=?(MyClassInterface)?clz.newInstance();??obj.say();??
這里客戶端通過自定義的 ClassLoader 變量名loader, load一個名字叫?"classload.MyClassBase" 的 Class, 通過 newInstance()方法 new 出它的一個obj, 并強制轉換成上面定義的接口類型?MyClassInterface。 注意:在客戶端代碼中是沒有?MyClassBase 這個類。自定義的 loader 會 通過指定的 name ?參數 “classload.MyClassBase”, 去找到加密后的文件?cipher/CipherMyClassBase.class, 并把它解密,返回 MyClassBase 的class實例。
現在看看 class文件加密、解密的代碼:
[java]?view plaincopy
package?classload;????import?java.io.BufferedInputStream;??import?java.io.BufferedOutputStream;??import?java.io.ByteArrayOutputStream;??import?java.io.File;??import?java.io.FileInputStream;??import?java.io.FileOutputStream;??import?java.io.IOException;????public?class?MyCipher?{????????public?static?void?main(String[]?args)?{????????????String[]?srcFileElement?=?{?System.getProperty("user.dir"),?"bin",?"classload",?"MyClassBase.class"?};??????????enCipherClass(String.join(File.separator,?srcFileElement));??????}????????public?static?String?enCipherClass(String?path)?{??????????File?classFile?=?new?File(path);??????????if?(!classFile.exists())?{??????????????System.out.println("File?does?not?exist!");??????????????return?null;??????????}????????????String?cipheredClass?=?classFile.getParent()?+?File.separator?+?"Cipher"?+?classFile.getName();??????????System.out.println("enCipherClass()?cipheredClass="?+?cipheredClass);????????????try?(BufferedInputStream?is?=?new?BufferedInputStream(new?FileInputStream(classFile));??????????????????BufferedOutputStream?out?=?new?BufferedOutputStream(new?FileOutputStream(cipheredClass));)?{????????????????int?data?=?0;??????????????while?((data?=?is.read())?!=?-1)?{??????????????????out.write(data?^?0xFF);??????????????}????????????????out.flush();??????????????is.close();??????????????out.close();??????????}?catch?(IOException?e)?{????????????????e.printStackTrace();??????????}??????????return?cipheredClass;??????}????????public?static?byte[]?deCihperClass(String?path)?{??????????File?file?=?new?File(path);??????????if?(!file.exists())?{??????????????System.out.println("deCihperClass()?File:"?+?path?+?"?not?found!");??????????????return?null;??????????}????????????System.out.println("deCihperClass()?path="?+?path);????????????try?(BufferedInputStream?in?=?new?BufferedInputStream(new?FileInputStream(file));)?{??????????????ByteArrayOutputStream?out?=?new?ByteArrayOutputStream();??????????????int?data?=?0;??????????????while?((data?=?in.read())?!=?-1)?{??????????????????out.write(data?^?0xFF);??????????????}??????????????in.close();??????????????out.flush();??????????????out.close();????????????????return?out.toByteArray();????????????}?catch?(IOException?e)?{??????????????e.printStackTrace();??????????}????????????return?null;??????}????}??
這里,僅僅是示例,加解密的算法非常簡單,?加密算法是把原.class 文件的每一個字節和 0xFF 異或, 對應的解密方法就是 加密后的字節和 0xFF異或,數學原理為: A^B^B = A。?
上面代碼中,加密方法enCipherClass 根據輸入的XXX.class文件名, 產生一個加密后的CipherXXX.class 文件, 這里把classload\MyClassBase.class ?變為?classload\CipherMyClassBase.class。?
解密方法?deCihperClass 把輸入的 class 文件, 變為 byte [] 返回。
運行加密算法后,需要手工把 ?classload\CipherMyClassBase.class 復制到目錄?cipher/, 并刪除目錄?classload\下的?CipherMyClassBase.class ?和?MyClassBase.class。
?
自定義ClassLoader的代碼:
[java]?view plaincopy
ClassLoader?loader?=?new?ClassLoader()?{??????@Override??????public?Class<?>?findClass(String?name)?{????????????System.out.println("findClass()?name?=?"?+?name);????????????String?baseName?=?name.substring(name.lastIndexOf('.')?+?1);????????????String[]?fileNameElements?=?{?System.getProperty("user.dir"),?"cipher",??????????????????"Cipher"?+?baseName?+?".class"?};??????????byte[]?data?=?MyCipher.deCihperClass(String.join(File.separator,?fileNameElements));????????????Class<?>?clz?=?defineClass(name,?data,?0,?data.length);??????????return?clz;??????}??};??
這里采用匿名內部類的方式定義自己的 ClassLoader, 定義自己的ClassLoader,只需要繼承?ClassLoader, 并覆寫方法?findClass 即可。
完整的 客戶端代碼:
[java]?view plaincopy
package?classload;??import?java.io.File;??public?class?ClassLoadTest?{??????public?static?void?main(String[]?args)?throws?Exception?{??????????ClassLoader?loader?=?new?ClassLoader()?{??????????????@Override??????????????public?Class<?>?findClass(String?name)?{????????????????????System.out.println("findClass()?name?=?"?+?name);????????????????????String?baseName?=?name.substring(name.lastIndexOf('.')?+?1);????????????????????String[]?fileNameElements?=?{?System.getProperty("user.dir"),?"cipher",??????????????????????????"Cipher"?+?baseName?+?".class"?};??????????????????byte[]?data?=?MyCipher.deCihperClass(String.join(File.separator,?fileNameElements));????????????????????Class<?>?clz?=?defineClass(name,?data,?0,?data.length);??????????????????return?clz;??????????????}??????????};??????????Class<?>?clz?=?loader.loadClass("classload.MyClassBase");??????????System.out.println("loaded?class:"?+?clz.getName()?+?"?by?"?+?clz.getClassLoader());??????????MyClassInterface?obj?=?(MyClassInterface)?clz.newInstance();??????????obj.say();??????}??}??
最后,需要注意的一點,客戶端通過自定義的ClassLoader ?像如下代碼加載類:
[java]?view plaincopy
loader.loadClass("classload.MyClassBase");??
自定義的 ClassLoader 會先把 類加載操作先委派給它的parent, 也就是系統默認的類加載器, 如果系統默認的類加載器,找不到 ?classload.MyClassBase 這個類,才會調用自己的類加載器,如果classpath下有classload.MyClassBase 這個類,系統默認的類加載器就會找到這個類, 那么自己的類加載器是不會調用的,所以前面說過,需要把classload/MyClassBase.class這個文件刪除,自己的類加載器才會起作用。
運行代碼看輸出:
[plain]?view plaincopy
findClass()?name?=?classload.MyClassBase??deCihperClass()?path=C:\Users\myname\myworkspace\Demo\cipher\CipherMyClassBase.class??loaded?class:classload.MyClassBase?by?classload.ClassLoadTest$1@6d06d69c??Hello?World!??
從上面的輸出看,使用的類加載器為我們自定的那個:
[plain]?view plaincopy
classload.ClassLoadTest$1@6d06d69c??
如果,把MyClassBase.class 放回到 bin/classload/MyClassBase.class, 輸出就變了:
[plain]?view plaincopy
loaded?class:classload.MyClassBase?by?sun.misc.Launcher$AppClassLoader@73d16e93??Hello?World!??
這個時候,使用的類加載器為:
[plain]?view plaincopy
sun.misc.Launcher$AppClassLoader@73d16e93??
我的 eclipse目錄結構:
只需關注 classload這個package和cipher目錄, 其它包與本文無關。
總結
以上是生活随笔為你收集整理的利用自定义的 ClassLoader 加密 Java Class 文件的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。