<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java安全之ClassLoader

          共 10364字,需瀏覽 21分鐘

           ·

          2021-10-03 01:06

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          1、類加載機(jī)制

          Java中的源碼.java后綴文件會(huì)在運(yùn)行前被編譯成.class后綴文件,文件內(nèi)的字節(jié)碼的本質(zhì)就是一個(gè)字節(jié)數(shù)組 ,它有特定的復(fù)雜的內(nèi)部格式,Java類初始化的時(shí)候會(huì)調(diào)用java.lang.ClassLoader加載字節(jié)碼,.class文件中保存著Java代碼經(jīng)轉(zhuǎn)換后的虛擬機(jī)指令,當(dāng)需要使用某個(gè)類時(shí),虛擬機(jī)將會(huì)加載它的.class文件,并創(chuàng)建對應(yīng)的class對象,將class文件加載到虛擬機(jī)的內(nèi)存,而在JVM中類的查找與裝載就是由ClassLoader完成的,而程序在啟動(dòng)的時(shí)候,并不會(huì)一次性加載程序所要用的所有class文件,而是根據(jù)程序的需要,來動(dòng)態(tài)加載某個(gè)class文件到內(nèi)存當(dāng)中的,從而只有class文件被載入到了內(nèi)存之后,才能被其它c(diǎn)lass所引用。所以ClassLoader就是用來動(dòng)態(tài)加載class文件到內(nèi)存當(dāng)中用的。

          2、類加載方式

          Java類加載方式分為顯式和隱式

          顯式:利用反射來加載一個(gè)類

          隱式:通過ClassLoader來動(dòng)態(tài)加載,new 一個(gè)類或者 類名.方法名返回一個(gè)類

          示例代碼

          @Test
          public?void?loadClassTest()?throws?Exception?{
          ????//1、反射加載
          ????Class?aClass?=?Class.forName("java.lang.Runtime");
          ????System.out.println(aClass.getName());

          ????//2、ClassLoader加載
          ????Class?aClass1?=?ClassLoader.getSystemClassLoader().loadClass("java.lang.ProcessBuilder");
          ????System.out.println(aClass1.getName());

          }

          那也就是其實(shí)可以通過ClassLoader.loadClass()代替Class.forName()來獲取某個(gè)類的class對象。

          3、ClassLoader

          ClassLoader(類加載器)主要作用就是將class文件讀入內(nèi)存,并為之生成對應(yīng)的java.lang.Class對象

          JVM中存在3個(gè)內(nèi)置ClassLoader:

          1. BootstrapClassLoader 啟動(dòng)類加載器 負(fù)責(zé)加載 JVM 運(yùn)行時(shí)核心類,這些類位于 JAVA_HOME/lib/rt.jar 文件中,我們常用內(nèi)置庫 java.xxx.* 都在里面,比如 java.util.*、java.io.*、java.nio.*、java.lang.*?等等。

          2. ExtensionClassLoader 擴(kuò)展類加載器 負(fù)責(zé)加載 JVM 擴(kuò)展類,比如 swing 系列、內(nèi)置的 js 引擎、xml 解析器 等等,這些庫名通常以 javax 開頭,它們的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中

          3. AppClassLoader 系統(tǒng)類加載器 才是直接面向我們用戶的加載器,它會(huì)加載 Classpath 環(huán)境變量里定義的路徑中的 jar 包和目錄。我們自己編寫的代碼以及使用的第三方 jar 包通常都是由它來加載的。

          除了Java自帶的ClassLoader外,還可以自定義ClassLoader,自定義的ClassLoader都必須繼承自java.lang.ClassLoader類,也包括Java提供的另外二個(gè)ClassLoader(Extension ClassLoader和App ClassLoader)在內(nèi),但是Bootstrap ClassLoader不繼承自ClassLoader,因?yàn)樗皇且粋€(gè)普通的Java類,底層由C++編寫,已嵌入到了JVM內(nèi)核當(dāng)中,當(dāng)JVM啟動(dòng)后,Bootstrap ClassLoader也隨著啟動(dòng),負(fù)責(zé)加載完核心類庫后,并構(gòu)造Extension ClassLoader和App ClassLoader類加載器。

          4、類加載流程

          類加載指的是在.java文件編譯成.class字節(jié)碼文件后,當(dāng)需要使用某個(gè)類時(shí),虛擬機(jī)將會(huì)加載它的.class文件,將.class文件讀入內(nèi)存,并在內(nèi)存中為之創(chuàng)建一個(gè)java.lang.Class對象。但是實(shí)現(xiàn)步驟看起來會(huì)比較空洞和概念化,暫時(shí)不去深入研究,理解類加載是做什么的并了解加載過程即可。后續(xù)有剛需再去深入。

          類加載大致分為三個(gè)步驟:加載、連接、初始化。

          0x01 加載

          類加載指的是將class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)java.lang.Class對象,即程序中使用任何類時(shí),也就是任何類在加載進(jìn)內(nèi)存時(shí),系統(tǒng)都會(huì)為之建立一個(gè)java.lang.Class對象,這個(gè)Class對象包含了該類的所有信息,如Filed,Method等,系統(tǒng)中所有的類都是java.lang.Class的實(shí)例。
          類的加載由類加載器完成,JVM提供的類加載器叫做系統(tǒng)類加載器,此外還可以通過自定義類加載器加載。
          通??梢杂萌缦聨追N方式加載類的二進(jìn)制數(shù)據(jù):

          從本地文件系統(tǒng)加載class文件。

          從JAR包中加載class文件,如JAR包的數(shù)據(jù)庫啟驅(qū)動(dòng)類。

          通過網(wǎng)絡(luò)加載class文件。

          把一個(gè)Java源文件動(dòng)態(tài)編譯并執(zhí)行加載。

          0x02 鏈接

          鏈接階段負(fù)責(zé)把類的二進(jìn)制數(shù)據(jù)合并到JRE中,其又可分為如下三個(gè)階段:

          1. 驗(yàn)證:確保加載的類信息符合JVM規(guī)范,無安全方面的問題。

          2. 準(zhǔn)備:為類的靜態(tài)Field分配內(nèi)存,并設(shè)置初始值。

          3. 解析:將類的二進(jìn)制數(shù)據(jù)中的符號(hào)引用替換成直接引用。

          0x03 初始化

          類加載最后階段,若該類具有超類,則對其進(jìn)行初始化,執(zhí)行靜態(tài)初始化器和靜態(tài)初始化成員變量(如前面只初始化了默認(rèn)值的static變量將會(huì)在這個(gè)階段賦值,成員變量也將被初始化

          5、雙親委派機(jī)制

          基本概念

          前面提到了Java自帶3個(gè)ClassLoader,包括我們也可以實(shí)現(xiàn)自定義ClassLoader完成類加載,但是具體某個(gè)類的加載用的是哪個(gè)ClassLoader呢。這里涉及到一個(gè)雙親委派機(jī)制(PS:這個(gè)我看網(wǎng)上有講的是委托也有是委派,個(gè)人覺得委派好聽,先這么叫著:D)

          這里丟個(gè)圖,基本概述了雙親委派機(jī)制(先走藍(lán)色箭頭再走紅色箭頭)

          雙親委派簡單理解:向上委派,向下加載

          當(dāng)一個(gè).class文件要被加載時(shí)。不考慮我們自定義類加載器,首先會(huì)在AppClassLoader中檢查是否加載過,如果有那就無需再加載了。如果沒有,那么會(huì)拿到父加載器,然后調(diào)用父加載器的loadClass方法。父類中同理也會(huì)先檢查自己是否已經(jīng)加載過,如果沒有再往上。注意這個(gè)類似遞歸的過程,直到到達(dá)Bootstrap classLoader之前,都是在檢查是否加載過,并不會(huì)選擇自己去加載。直到BootstrapClassLoader,已經(jīng)沒有父加載器了,這時(shí)候開始考慮自己是否能加載了(向上委派); 如果自己無法加載,會(huì)下沉到子加載器去加載,一直到最底層(向下加載)。如果沒有任何加載器能加載,就會(huì)拋出ClassNotFoundException異常。

          為什么?

          那么為什么加載類的時(shí)候需要雙親委派機(jī)制呢?

          采用雙親委派模式的是好處是Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級(jí)的層次關(guān)系,通過這種層級(jí)關(guān)可以避免類的重復(fù)加載,當(dāng)父親已經(jīng)加載了該類時(shí),就沒有必要子ClassLoader再加載一次。

          其次是,如果有人想替換系統(tǒng)級(jí)別的類:String.java。篡改它的實(shí)現(xiàn),在這種機(jī)制下這些系統(tǒng)的類已經(jīng)被Bootstrap classLoader加載過了(為什么?因?yàn)楫?dāng)一個(gè)類需要加載的時(shí)候,最先去嘗試加載的就是BootstrapClassLoader),所以其他類加載器并沒有機(jī)會(huì)再去加載,從一定程度上防止了危險(xiǎn)代碼的植入。

          6、自定義ClassLoader

          先看下ClassLoader這個(gè)類中的核心方法

          ClassLoader核心方法

          1. loadClass(加載指定的Java類)

            一般實(shí)現(xiàn)這個(gè)方法的步驟是:執(zhí)行findLoadedClass(String)去檢測這個(gè)class是不是已經(jīng)加載過了。
            執(zhí)行父加載器的loadClass方法。如果父加載器為null則jvm內(nèi)置的加載器去替代,也就是Bootstrap ClassLoader。這也解釋了ExtClassLoader的parent為null,但仍然說Bootstrap ClassLoader是它的父加載器。如果向上委托父加載器沒有加載成功;則通過findClass(String)查找。
            如果class在上面的步驟中找到了,參數(shù)resolve又是true的話那么loadClass()又會(huì)調(diào)用resolveClass(Class)這個(gè)方法來生成最終的Class對象。

          2. findClass(查找指定的Java類)

          3. findLoadedClass(查找JVM已經(jīng)加載過的類)

          4. defineClass(定義一個(gè)Java類)

          5. resolveClass(鏈接指定的Java類)

          編寫自定義ClassLoader步驟

          1、編寫一個(gè)類繼承ClassLoader抽象類;

          2、重寫findClass()方法;

          3、在findClass()方法中調(diào)用defineClass()方法即可實(shí)現(xiàn)自定義ClassLoader;

          0x01 編寫測試類

          package?classloader;

          public?class?test?{
          ????public?String?hello(){
          ????????return?"hello,?CoLoo!";
          ????}
          }

          0x02 編譯為.class文件

          0x03?.class轉(zhuǎn)換bytes

          public?class?ByteClass?{
          ????public?static?void?main(String[]?args)?throws?IOException?{
          ????????FileInputStream?fis?=?new?FileInputStream("test.class");
          ????????byte[]?classBytes?=?IOUtils.readFully(fis,?-1,?false);
          ????????System.out.println(Arrays.toString(classBytes));
          ????}
          }

          Output:

          [-54, -2, -70, -66, 0, 0, 0, 52, 0, 17, 10, 0, 4, 0, 13, 8, 0, 14, 7, 0, 15, 7, 0, 16, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 5, 104, 101, 108, 108, 111, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 9, 116, 101, 115, 116, 46, 106, 97, 118, 97, 12, 0, 5, 0, 6, 1, 0, 13, 104, 101, 108, 108, 111, 44, 32, 67, 111, 76, 111, 111, 33, 1, 0, 16, 99, 108, 97, 115, 115, 108, 111, 97, 100, 101, 114, 47, 116, 101, 115, 116, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 0, 33, 0, 3, 0, 4, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, 0, 7, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 3, 0, 1, 0, 9, 0, 10, 0, 1, 0, 7, 0, 0, 0, 27, 0, 1, 0, 1, 0, 0, 0, 3, 18, 2, -80, 0, 0, 0, 1, 0, 8, 0, 0, 0, 6, 0, 1, 0, 0, 0, 5, 0, 1, 0, 11, 0, 0, 0, 2, 0, 12]

          0x04 自定義ClassLoader

          package?classloader;

          import?java.lang.reflect.Method;

          public?class?ClassLoaderTest?extends?ClassLoader?{

          ????private?static?String?className?=?"classloader.test";
          ????//轉(zhuǎn)換byte后的字節(jié)碼
          ????private?static?byte[]?classBytes?=?new?byte[]{54,?-2,?-70,?-66,?0,?0,?0,?52,?0,?17,?10,?0,?4,?0,?13,?8,?0,?14,?7,?0,?15,?7,?0,?16,?1,?0,?6,?60,?105,?110,?105,?116,?62,?1,?0,?3,?40,?41,?86,?1,?0,?4,?67,?111,?100,?101,?1,?0,?15,?76,?105,?110,?101,?78,?117,?109,?98,?101,?114,?84,?97,?98,?108,?101,?1,?0,?5,?104,?101,?108,?108,?111,?1,?0,?20,?40,?41,?76,?106,?97,?118,?97,?47,?108,?97,?110,?103,?47,?83,?116,?114,?105,?110,?103,?59,?1,?0,?10,?83,?111,?117,?114,?99,?101,?70,?105,?108,?101,?1,?0,?9,?116,?101,?115,?116,?46,?106,?97,?118,?97,?12,?0,?5,?0,?6,?1,?0,?13,?104,?101,?108,?108,?111,?44,?32,?67,?111,?76,?111,?111,?33,?1,?0,?16,?99,?108,?97,?115,?115,?108,?111,?97,?100,?101,?114,?47,?116,?101,?115,?116,?1,?0,?16,?106,?97,?118,?97,?47,?108,?97,?110,?103,?47,?79,?98,?106,?101,?99,?116,?0,?33,?0,?3,?0,?4,?0,?0,?0,?0,?0,?2,?0,?1,?0,?5,?0,?6,?0,?1,?0,?7,?0,?0,?0,?29,?0,?1,?0,?1,?0,?0,?0,?5,?42,?-73,?0,?1,?-79,?0,?0,?0,?1,?0,?8,?0,?0,?0,?6,?0,?1,?0,?0,?0,?3,?0,?1,?0,?9,?0,?10,?0,?1,?0,?7,?0,?0,?0,?27,?0,?1,?0,?1,?0,?0,?0,?3,?18,?2,?-80,?0,?0,?0,?1,?0,?8,?0,?0,?0,?6,?0,?1,?0,?0,?0,?5,?0,?1,?0,?11,?0,?0,?0,?2,?0,?12};


          ????@Override
          ????protected?Class?findClass(String?name)?throws?ClassNotFoundException?{
          ????????//只處理classloader.test類
          ????????if?(name.equals(className))?{
          ????????????//調(diào)用definClass將一個(gè)字節(jié)流定義為一個(gè)類。
          ????????????return?defineClass(className,?classBytes,?0,?classBytes.length);
          ????????}
          ????????return?super.findClass(name);
          ????}

          ????public?static?void?main(String[]?args)?throws?Exception?{
          ????????//創(chuàng)建加載器
          ????????ClassLoaderTest?clt?=?new?ClassLoaderTest();
          ????????//使用我們自定義的類去加載className
          ????????Class?clazz?=?clt.loadClass(className);
          ????????//反射創(chuàng)建test類對象
          ????????Object?test?=?clazz.newInstance();
          ????????//反射獲取方法
          ????????Method?method?=?test.getClass().getMethod("hello");
          ????????//反射去調(diào)用執(zhí)行方法
          ????????String?str?=?(String)?method.invoke(test);
          ????????System.out.println(str);

          ????}
          }

          執(zhí)行了test類的hello方法

          7、一些思考

          上面自定義ClassLoader流程也可以小結(jié)一下

          1. 準(zhǔn)備自定義類,編譯為.class文件

          2. 將.class文件內(nèi)容專為bytes數(shù)組

          3. 自定義ClassLoader,繼承ClassLoader類,重寫findClass()方法,在方法中定義對指定類的處理流程

          4. 主函數(shù)創(chuàng)建自定義ClassLoader對象并loadClass()指定類,如果自定義的ClassLoader完成了加載則會(huì)獲得該類的class對象,后續(xù)可通過反射來深入利用(如執(zhí)行某個(gè)方法)。

          那ClassLoader對于安全來說能做什么?

          1、個(gè)人這里目前想到的是代替Class.forName(),通過ClassLoader.loadClass()獲取class對象

          @Test
          public?void?classLoaderRuntime()?throws?Exception?{
          ????Class?aClass?=?ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime");
          ????Runtime?runtime?=?(Runtime)?aClass.getMethod("getRuntime").invoke(aClass);
          ????runtime.exec("open?-a?Calculator");

          }

          2、加載惡意類

          如webshell中(之前有次攻防捕捉到一個(gè)webshell里面用到了classloader)或者內(nèi)存馬中應(yīng)該也可以用到。

          惡意類加載還是有必要深入學(xué)習(xí)一下,給后續(xù)學(xué)習(xí)內(nèi)存馬和反序列化payload打個(gè)基礎(chǔ)。

          首先上面提到了,關(guān)于顯隱式加載類是有些區(qū)別的,顯示加載時(shí)(反射)可以觸發(fā)該類的初始化從而調(diào)用靜態(tài)代碼塊執(zhí)行,主要是因?yàn)槭褂胘ava.lang.reflect對類進(jìn)行反射調(diào)用時(shí),如果該類沒有初始化會(huì)先進(jìn)行類的初始化;而隱式加載,如new,ClassLoader.getSystemClassLoader.loadClass(),不會(huì)初始化類也就不執(zhí)行靜態(tài)代碼塊中的內(nèi)容。

          下面簡單測試了幾個(gè)可能會(huì)用到的觸發(fā)類初始化的類加載方式,簡單列舉下。

          1. 默認(rèn)java.lang.ClassLoader

            • loadClass() + newInstance()

            • Class.forName()

            • Class.forName(ClassName, true, ClassLoaderName) + newInstance()

          2. org.apache.bcel.util.ClassLoader?也是fj經(jīng)常用到的,需要注意的是在JDK8u251之后就沒有ClassLoader這個(gè)類了

          3. URLClassLoader

          4. new

          Classloader

          package?classloader;
          import?com.sun.org.apache.bcel.internal.Repository;
          import?com.sun.org.apache.bcel.internal.classfile.JavaClass;
          import?com.sun.org.apache.bcel.internal.classfile.Utility;
          import?com.sun.org.apache.bcel.internal.util.ClassLoader;


          public?class?ClassLoaderBaseClass?{
          ????public?static?void?main(String[]?args)?throws?Exception?{
          ????????//?1、ClassLoader.getSystemClassLoader().loadClass()?+?反射newInstance()?[+]
          ????????ClassLoader.getSystemClassLoader().loadClass("classloader.CalcBaseClass2").newInstance();

          ????????//?0x02?new?[+]
          ????????CalcBaseClass2?calcBaseClass2?=?new?CalcBaseClass2();

          ????????//?0x03?Class.forName()?[+]
          ????????Class.forName("classloader.CalcBaseClass2");

          ????????//?0x04?ClassLoader.getSystemClassLoader().loadClass()?[-]
          ????????ClassLoaderTest.getSystemClassLoader().loadClass("classloader.CalcBaseClass2");

          ????????//?0x05?Class.forName(className,?true,?ClassLoaderName)
          ????????Class.forName("classloader.CalcBaseClass",?true,?java.lang.ClassLoader.getSystemClassLoader());

          ????????//?0x06?bcel
          ????????JavaClass?clazz?=?Repository.lookupClass(CalcBaseClass2.class);
          ????????String?code?=?Utility.encode(clazz.getBytes(),?true);
          ????????System.out.println(code);

          ????????new?ClassLoader().loadClass("$$BCEL$$"?+?code).newInstance();

          ????}
          }

          calc

          package?classloader;

          import?java.io.IOException;

          public?class?CalcBaseClass2?{
          ????static?{
          ????????try?{
          ????????????Runtime.getRuntime().exec("open?-a?Calculator");
          ????????}?catch?(IOException?e)?{
          ????????????e.printStackTrace();
          ????????}
          ????}

          }



          ? 作者?|??CoLo

          來源 |??cnblogs.com/CoLo/p/15339193.html

          加鋒哥微信:?java3459??
          圍觀鋒哥朋友圈,每天推送Java干貨!

          瀏覽 44
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大操在线 | 伊人久久视频 | 在线sm调教视频网站 | 亚洲熟妇一区二区三区 | 欧美性爱一级棒 |