以前面試的時候有許多面試官問類加載器相關的問題,因此這是一個很重要的知識點。並且對於高級Android研發來說,懂得更多類加載相關的東西,對開發也會有不少的幫助,好比熱更新,類加密等。
其實筆者對類加密比較感興趣,就稍稍調研了一下。類加密的實際上是爲了防止APP被反編譯,防止反編譯的方法有不少種,好比混淆,加固等。本身對類文件進行加密,並自定義類加載器也是一種辦法:java
首先咱們的代碼打包編譯以後會變成難以讀懂的二進制字節碼,而且變成.class文件。可是簡單的APP編譯出來以後能夠被反編譯,甚至你寫的代碼完徹底全被暴露。你的代碼被抄襲,被複制都是小事,重要的實際上是大家APP的商業信息有可能被泄露!
下面是一個簡單的例子被反編譯的場景:
面試
因此爲了避免讓用戶輕易的反編譯出源代碼文件,就要對.class文件進行加密,再經過特殊的加載類的方式解密,並將這個類加載到內存中。算法
首先說加密,加密無非就是把.class字節碼文件進行一些變換,這裏面就涉及密碼學的知識了!加密的方式有不少種,要想提升保密性,能夠考慮DES,AES,RSA。一旦加密算法的源碼被公開,其實破解也就是很簡單的事情了,因此建議你們仍是用高安全性的密碼系統,到時候及時的更換密鑰。就能必定程度上增長破解難度。安全
有了加密算法,接下來就是加密一個字節碼文件了:性能優化
private static File file = null;
private static String path = null;
// 讀取已經編譯好的正常的class字節碼文件
public static void readClass(String filePath) throws Exception {
file = new File(filePath);
path = filePath;
}
// 加密生成已加密的class字節碼文件
public static void encrypt() throws Exception {
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(path.substring(0,
path.lastIndexOf(".class"))
+ "附件.class");
byte[] b = new byte[1024];
int ch = 0;
while ((ch = fis.read(b)) != -1) {
// 變換b
b=crypt(b, "encrypt");
fos.write(b, 0, ch);
}
}
複製代碼
這時候,調用readClass方法和encrypt方法,就能在本來XX.class文件的目錄下生成XX附件.class。這時候,咱們就使用這個副本的字節碼文件,刪除原來的,下次運行的時候再解密,這樣就好了。架構
對類加密以後,須要用本身的方式把類再加載出來。正常的時候我們寫的類也須要被類加載器加載到內存中。因此,就涉及到類加載器的知識了:ide
系統默認三個類加載器,分別是:BootStrap,ExtClassLoader,AppClassLoader。那麼這幾個類加載器有什麼區別呢?性能
首先類加載器有父子關係。BootStrap是爺爺(用C++編寫,主要負責加載jre/lib/rt.jar),ExtClassLoader是爸爸(主要用於加載JRE/lib/ext/*.jar),AppClassLoader是兒子(用於負責加載ClassPath指定目錄下的全部jar)。學習
因此咱們通常寫的class文件都是AppClassLoader加載的。那假如咱們寫了一個類,咱們把這個類複製一份,放到ExtClassLoader目錄下,那麼類加載器會怎麼加載呢?這就要提到類加載器的委託機制了。優化
類加載器的委託機制:當一個線程調用一個類的時候,首先用當前線程的類加載器去加載這個類,這個類加載器一開始不加載,會通知他的上一級類加載器去加載,等到了BootStrap加載器的時候,若是沒有就再讓調用的下級加載器去加載。若是都沒有就報ClassNotFoundException異常。也能夠直接指定類加載器去加載。
因此咱們能夠本身定義一個類加載器,讓這個讓這個類加載器去加載咱們加密過的類。自定義類加載器須要繼承ClassLoader類,而且重寫findClass方法。
class MyClassLoader extends ClassLoader {
private String path = null;
// 設置自定義類加載器的目錄
public MyClassLoader(String path) {
this.path = path;
}
/* * findClass和loadClass的區別? */
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
File f = new File(path, name.substring(name.lastIndexOf('.') + 1)
+ ".class");
FileInputStream fis = new FileInputStream(f);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int ch = 0;
while ((ch = fis.read()) != -1) {
bos.write(ch);
}
byte[] buf = bos.toByteArray();
//解密.class
buf = Z1Encrypt.crypt(buf, "decrypt");
fis.close();
bos.close();
//根據字節碼返回這個類
return defineClass(name, buf, 0, buf.length);
} catch (Exception e) {
throw new ClassNotFoundException(name + " is not found!");
}
}
}
複製代碼
接着咱們用咱們的自定義類加載器去解密加載咱們加密好的字節碼文件
public static void main(String[] args) throws Exception {
/*Z1Encrypt.readClass("F:\\WorkSpace\\classLoader\\DemoTemp.class"); Z1Encrypt.encrypt(); */
MyClassLoader mcl = new MyClassLoader("F:\\WorkSpace\\classLoader\\");
Class clazz = mcl.findClass("DemoTemp");
Method me = clazz.getMethod("say",null);
Object obj = clazz.newInstance();
me.invoke(obj, null);
}
複製代碼
後來成功的解密了字節碼文件,並將其加載到內存中,併成功調用了相關的方法了。
以上是一個很是簡單的代碼加密,並自定義類加載器的方法,僅供學習其中思路和知識。
喜歡的話記得點個讚的