最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java安全-ClassLoader

 更新時間:2022年01月14日 09:36:40   作者:Zh1z3ven  
這篇文章主要介紹了Java安全ClassLoader,Java類初始化的時候會調用java.lang.ClassLoader加載字節(jié)碼,ClassLoader就是用來動態(tài)加載class文件到內存當中用的,下面詳細內容,需要的小伙伴可以參考一下

1.類加載機制

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

2.類加載方式

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

顯式:利用反射來加載一個類

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

示例代碼:

@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());

}

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

3.ClassLoader

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

JVM中存在3個內置ClassLoader:

  • BootstrapClassLoader 啟動類加載器 負責加載 JVM 運行時核心類,這些類位于 JAVA_HOME/lib/rt.jar 文件中,我們常用內置庫 java.xxx.* 都在里面,比如 java.util.*、java.io.*、java.nio.*、java.lang.* 等等。
  • ExtensionClassLoader 擴展類加載器 負責加載 JVM 擴展類,比如 swing 系列、內置的 js 引擎、xml 解析器 等等,這些庫名通常以 javax 開頭,它們的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中
  • AppClassLoader 系統(tǒng)類加載器 才是直接面向我們用戶的加載器,它會加載 Classpath 環(huán)境變量里定義的路徑中的 jar 包和目錄。我們自己編寫的代碼以及使用的第三方 jar 包通常都是由它來加載的。

除了Java自帶的ClassLoader外,還可以自定義ClassLoader,自定義的ClassLoader都必須繼承自java.lang.ClassLoader類,也包括Java提供的另外二個ClassLoader(Extension ClassLoader和App ClassLoader)在內,但是Bootstrap ClassLoader不繼承自ClassLoader,因為它不是一個普通的Java類,底層由C++編寫,已嵌入到了JVM內核當中,當JVM啟動后,Bootstrap ClassLoader也隨著啟動,負責加載完核心類庫后,并構造Extension ClassLoaderApp ClassLoader類加載器。

4.類加載流程

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

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

加載

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

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

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

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

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

鏈接

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

驗證:確保加載的類信息符合JVM規(guī)范,無安全方面的問題。
準備:為類的靜態(tài)Field分配內存,并設置初始值。
解析:將類的二進制數(shù)據(jù)中的符號引用替換成直接引用。

初始化

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

5.雙親委派機制

基本概念

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

這里丟個圖,基本概述了雙親委派機制(先走藍色箭頭再走紅色箭頭)

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

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

為什么呢?

那么為什么加載類的時候需要雙親委派機制呢?

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

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

自定義ClassLoader

先看下ClassLoader這個類中的核心方法

ClassLoader核心方法:

  • loadClass(加載指定的Java類)

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

  • findClass(查找指定的Java類)
  • findLoadedClass(查找JVM已經(jīng)加載過的類)
  • defineClass(定義一個Java類)
  • resolveClass(鏈接指定的Java類)

編寫自定義ClassLoader步驟:

  • 1、編寫一個類繼承ClassLoader抽象類;
  • 2、重寫findClass()方法;
  • 3、在findClass()方法中調用defineClass()方法即可實現(xiàn)自定義ClassLoader;

 編寫測試類

package classloader;

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

編譯為.class文件

class轉換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]

自定義ClassLoader

package classloader;

import java.lang.reflect.Method;

public class ClassLoaderTest extends ClassLoader {

? ? private static String className = "classloader.test";
? ? //轉換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)) {
? ? ? ? ? ? //調用definClass將一個字節(jié)流定義為一個類。
? ? ? ? ? ? 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");
? ? ? ? //反射去調用執(zhí)行方法
? ? ? ? String str = (String) method.invoke(test);
? ? ? ? System.out.println(str);

? ? }
}

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

一些思考

上面自定義ClassLoader流程也可以小結一下:

  1. 準備自定義類,編譯為.class文件
  2. 將.class文件內容專為bytes數(shù)組
  3. 自定義ClassLoader,繼承ClassLoader類,重寫findClass()方法,在方法中定義對指定類的處理流程
  4. 主函數(shù)創(chuàng)建自定義ClassLoader對象并loadClass()指定類,如果自定義的ClassLoader完成了加載則會獲得該類的class對象,后續(xù)可通過反射來深入利用(如執(zhí)行某個方法)。

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

1、個人這里目前想到的是代替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中(之前有次攻防捕捉到一個webshell里面用到了classloader)或者內存馬中應該也可以用到。

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

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

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

簡單列舉下:

  • 默認java.lang.ClassLoader
  1. loadClass() + newInstance()
  2. Class.forName()
  3. Class.forName(ClassName, true, ClassLoaderName) + newInstance()
  • org.apache.bcel.util.ClassLoader 也是fj經(jīng)常用到的,需要注意的是在JDK8u251之后就沒有ClassLoader這個類了
  • URLClassLoader
  • 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();
? ? ? ? }
? ? }

}

到此這篇關于Java安全-ClassLoader的文章就介紹到這了,更多相關Java ClassLoader內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

參考:

https://www.cnblogs.com/nice0e3/p/13719903.html

http://code2sec.com/javafan-xu-lie-hua-lou-dong-xue-xi-shi-jian-liu-lei-de-jia-zai-ji-zhi-he-e-yi-lei-gou-zao.html

https://blog.csdn.net/javazejian/article/details/73413292

https://blog.csdn.net/CNAHYZ/article/details/82219210

https://blog.csdn.net/briblue/article/details/54973413

https://blog.csdn.net/codeyanbao/article/details/82875064

相關文章

  • Linux下用java -jar運行可執(zhí)行jar包的方法教程

    Linux下用java -jar運行可執(zhí)行jar包的方法教程

    這篇文章主要給大家介紹了在Linux下用java -jar運行可執(zhí)行jar包的方法教程,文中介紹的非常詳細,相信對大家的工作或者學習具有一定的參考學習價值,需要的朋友們下面來一起看看吧。
    2017-05-05
  • 基于Java編寫簡易的算式測試程序

    基于Java編寫簡易的算式測試程序

    本文將利用Java語言編寫一個簡易的算式測試程序,這個程序可以自動生成指定數(shù)量的加減乘三則運算題目,感興趣的小伙伴可以了解一下
    2022-05-05
  • Java?Calendar類使用之日期和時間處理指南

    Java?Calendar類使用之日期和時間處理指南

    這篇文章主要給大家介紹了關于Java?Calendar類使用之日期和時間處理指南的相關資料,Calendar類是Java中用于處理日期和時間的抽象類,它提供了一種獨立于特定日歷系統(tǒng)的方式來處理日期和時間,需要的朋友可以參考下
    2023-12-12
  • Spring Cloud Gateway打造可擴展的微服務網(wǎng)關

    Spring Cloud Gateway打造可擴展的微服務網(wǎng)關

    微服務網(wǎng)關是一個位于客戶端和后端微服務之間的服務器,用于處理所有與客戶端的通信,Spring Cloud Gateway都是一個值得考慮的選擇,它將幫助您更好地管理和保護您的微服務,感興趣的朋友一起看看吧
    2023-11-11
  • Java實現(xiàn)大文件的分片上傳與下載(springboot+vue3)

    Java實現(xiàn)大文件的分片上傳與下載(springboot+vue3)

    這篇文章主要為大家詳細介紹了java基于springboot+vue3如何大文件的分片上傳與下載,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2023-06-06
  • Java中HashMap和Hashtable的區(qū)別淺析

    Java中HashMap和Hashtable的區(qū)別淺析

    這篇文章主要介紹了Java中HashMap和Hashtable的區(qū)別淺析,本文總結了6條它們之間的不同之處,需要的朋友可以參考下
    2015-03-03
  • Java 實現(xiàn)協(xié)同過濾算法推薦算法的示例代碼

    Java 實現(xiàn)協(xié)同過濾算法推薦算法的示例代碼

    本文介紹了協(xié)同過濾算法的概念,包括基于用戶的協(xié)同過濾和基于物品的協(xié)同過濾,文章詳細解釋了數(shù)據(jù)準備、相似度計算以及如何在Java中實現(xiàn)這些算法,通過一個簡單的用戶-物品評分矩陣示例,展示了如何計算用戶和物品之間的相似度,并推薦未評分的物品,感興趣的朋友一起看看吧
    2025-02-02
  • Jenkins忘記密碼密碼重置操作步驟詳解

    Jenkins忘記密碼密碼重置操作步驟詳解

    這篇文章主要為大家介紹了Jenkins忘記密碼密碼重置操作步驟詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-09-09
  • Java版水果管理系統(tǒng)源碼

    Java版水果管理系統(tǒng)源碼

    這篇文章主要為大家詳細介紹了Java版水果管理系統(tǒng)源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Java線性表的順序表示及實現(xiàn)

    Java線性表的順序表示及實現(xiàn)

    這篇文章主要介紹了Java線性表的順序表示及實現(xiàn),順序表是在計算機內存中以數(shù)組的形式保存的線性表,線性表的順序存儲是指用一組地址連續(xù)的存儲單元依次存儲線性表中的各個元素、使得線性表中在邏輯結構上相鄰的數(shù)據(jù)元素存儲在相鄰的物理存儲單元中
    2022-07-07

最新評論

洛南县| 五指山市| 徐闻县| 汤阴县| 健康| 棋牌| 肥西县| 榆树市| 乐山市| 留坝县| 海兴县| 芷江| 临夏县| 治多县| 光泽县| 九江市| 盱眙县| 抚宁县| 芦溪县| 阿拉善盟| 晋宁县| 南城县| 兴义市| 左贡县| 潼关县| 铜陵市| 安陆市| 开化县| 江北区| 潼关县| 怀集县| 莒南县| 泰安市| 鹤庆县| 永福县| 梧州市| 左贡县| 林周县| 巢湖市| 新营市| 塘沽区|