Java反射

类加载机制

ClassLoader

一切的Java类都必须经过JVM加载后才能运行,而ClassLoader的主要作用就是Java类文件的加载。在JVM类加载器中最顶层的是Bootstrap ClassLoader(引导类加载器)Extension ClassLoader(扩展类加载器)App ClassLoader(系统类加载器)AppClassLoader是默认的类加载器,如果类加载时我们不指定类加载器的情况下,默认会使用AppClassLoader加载类,ClassLoader.getSystemClassLoader()返回的系统类加载器也是AppClassLoader
ClassLoader的核心方法:

  1. loadClass(加载指定的Java类)
  2. findClass(查找指定的Java类)
  3. findLoadedClass(查找JVM已经加载过的类)
  4. defineClass(定义一个Java类)
  5. resolveClass(链接指定的Java类)

Java类动态加载方式

构造一个恶意类Evil

import java.io.IOException;  
public class Evil {  
static {  
try {  
Runtime.getRuntime().exec(new String("calc"));  
} catch (IOException e) {  
throw new RuntimeException(e);  
}  
}  
}

反射加载(初始化)

class Main{  
public static void main(String[] args) throws ClassNotFoundException {  
Class.forName("Evil");  
}  
}

forName的两个重载

Class<?> forName(String name)
Class<?> forName(String name, **boolean** initialize, ClassLoader loader)

修改一下Evil类

public class Evil {  
static {  
System.out.println(1);  
}  //A
{  
System.out.println(2);  
}  //B
public Evil(){  
System.out.println(3);  
}  //C
  
}

进行反射加载

class Main{  
public static void main(String[] args) throws ClassNotFoundException {  
Class.forName("Evil").newInstance();  
}  
}

可以看到最先执行的是A,然后是B,最后是C
如果不实例化对象,

class Main{  
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {  
Class.forName("Evil");  
}  
}

那么只执行A,这就是类初始化

类加载器(不初始化)

class Main{  
public static void main(String[] args) throws ClassNotFoundException {  
ClassLoader c =ClassLoader.getSystemClassLoader();  
c.loadClass("Evil");  
}  
}

calc没有执行,想要执行命令,应该换种写法

class Main{  
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {  
ClassLoader c =ClassLoader.getSystemClassLoader();  
c.loadClass("Evil").newInstance();  
}  
}

使用类加载器加载恶意类,类初始化在实例化对象时执行

ClassLoader加载流程

  1. ClassLoader调用public Class<?> loadClass(String name)
  2. 调用findLoadedClass方法检查TestHelloWorld类是否已经初始化,如果JVM已初始化过该类则直接返回类对象。
  3. 如果创建当前ClassLoader时传入了父类加载器(new ClassLoader(父类加载器))就使用父类加载器,否则使用JVM的Bootstrap ClassLoader加载。
  4. 如果上一步无法加载,那么调用自身的findClass方法尝试加载。
  5. 如果当前的ClassLoader没有重写了findClass方法,那么直接返回类加载失败异常。如果当前类重写了findClass方法并通过传入的类名找到了对应的类字节码,那么应该调用defineClass方法去JVM中注册该类
  6. 如果调用loadClass的时候传入的resolve参数为true,那么还需要调用resolveClass方法链接类,默认为false。
  7. 返回一个被JVM加载后的java.lang.Class类对象。

自写ClassLoader

如果Evil类不在classpath,可以重写findClasss方法,在调用defineClass时传入Evil的字节码,实现类的加载
.class文件转byte[]

import java.io.File;  
import java.io.FileInputStream;  
import java.io.IOException;  
import java.util.Arrays;  
  
class Main{  
public static void main(String[] args) throws IOException {  
File f = new File("C:\\Users\\yehep\\IdeaProjects\\demo\\src\\Evil.class");  
byte[] bytesArray = new byte[(int) f.length()];  
FileInputStream fis = new FileInputStream(f);  
fis.read(bytesArray);  
fis.close();  
print(bytesArray);  
}  
public static void print(byte[] bytesArray){  
System.out.println(Arrays.toString(bytesArray));  
}  
}
public class EvilClassLoader extends ClassLoader{  
private static String name="Evil";  
private static byte[] EvilBytes = new byte[]{-54, -2, -70, -66, 0, 0, 0, 64, 0, 34, 10, 0, 2, 0, 3, 7, 0, 4, 12, 0, 5, 0, 6, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 10, 0, 8, 0, 9, 7, 0, 10, 12, 0, 11, 0, 12, 1, 0, 17, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 82, 117, 110, 116, 105, 109, 101, 1, 0, 10, 103, 101, 116, 82, 117, 110, 116, 105, 109, 101, 1, 0, 21, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 82, 117, 110, 116, 105, 109, 101, 59, 8, 0, 14, 1, 0, 4, 99, 97, 108, 99, 10, 0, 8, 0, 16, 12, 0, 17, 0, 18, 1, 0, 4, 101, 120, 101, 99, 1, 0, 39, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 80, 114, 111, 99, 101, 115, 115, 59, 7, 0, 20, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 73, 79, 69, 120, 99, 101, 112, 116, 105, 111, 110, 7, 0, 22, 1, 0, 26, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 82, 117, 110, 116, 105, 109, 101, 69, 120, 99, 101, 112, 116, 105, 111, 110, 10, 0, 21, 0, 24, 12, 0, 5, 0, 25, 1, 0, 24, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 84, 104, 114, 111, 119, 97, 98, 108, 101, 59, 41, 86, 7, 0, 27, 1, 0, 4, 69, 118, 105, 108, 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, 8, 60, 99, 108, 105, 110, 105, 116, 62, 1, 0, 13, 83, 116, 97, 99, 107, 77, 97, 112, 84, 97, 98, 108, 101, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 9, 69, 118, 105, 108, 46, 106, 97, 118, 97, 0, 33, 0, 26, 0, 2, 0, 0, 0, 0, 0, 2, 0, 1, 0, 5, 0, 6, 0, 1, 0, 28, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 29, 0, 0, 0, 6, 0, 1, 0, 0, 0, 2, 0, 8, 0, 30, 0, 6, 0, 1, 0, 28, 0, 0, 0, 84, 0, 3, 0, 1, 0, 0, 0, 23, -72, 0, 7, 18, 13, -74, 0, 15, 87, -89, 0, 13, 75, -69, 0, 21, 89, 42, -73, 0, 23, -65, -79, 0, 1, 0, 0, 0, 9, 0, 12, 0, 19, 0, 2, 0, 29, 0, 0, 0, 22, 0, 5, 0, 0, 0, 5, 0, 9, 0, 8, 0, 12, 0, 6, 0, 13, 0, 7, 0, 22, 0, 9, 0, 31, 0, 0, 0, 7, 0, 2, 76, 7, 0, 19, 9, 0, 1, 0, 32, 0, 0, 0, 2, 0, 33  
};  
@Override  
public Class<?> findClass(String name) throws ClassNotFoundException {  
return defineClass(name, EvilBytes, 0, EvilBytes.length);  
}  
}
class Main{  
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {  
EvilClassLoader e = new EvilClassLoader();  
e.loadClass("Evil").newInstance();  
  
}  
}

成功执行命令

反射

Java反射(Reflection)是Java非常重要的动态特性,通过使用反射我们不仅可以获取到任何类的成员方法(Methods)、成员变量(Fields)、构造方法(Constructors)等信息,还可以动态创建Java类实例、调用任意的类方法、修改任意的类成员变量值等。

获取Class对象

获取Class对象的方法

//通过类获取  
Class c1=Evil.class;  
//通过对象获取  
Evil evil = new Evil();  
Class c2 =evil.getClass();  
//反射获取  
Class c3 = Class.forName("Evil");  
//类加载器获取  
Class c4 = ClassLoader.getSystemClassLoader().loadClass("Evil");

反射java.lang.Runtime

对于一个Class对象,newInstance()方法是调用这个类的无参构造,而Runtime类的构造函数是私有的,Runtime是单例模式
反射java.lang.Runtime执行系统命令的两种方法

import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Method;  
  
class Main{  
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {  
Class rt = Class.forName("java.lang.Runtime");//反射获取Runtime类  
Method exec=rt.getMethod("exec",String.class);//获取exec方法  
Method getRuntime =rt.getMethod("getRuntime");//获取getRuntime方法  
Object rtObject = getRuntime.invoke(rt);//调用getRuntime方法,获取Runtime类的实例化对象  
exec.invoke(rtObject,"calc");//调用exec方法执行命令  
}  
}
import java.lang.reflect.Constructor;  
import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Method;  
  
class Main{  
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {  
Class rt = Class.forName("java.lang.Runtime");//反射获取Runtime类  
Constructor rtConstructor = rt.getDeclaredConstructor();  
rtConstructor.setAccessible(true);  //设置私有构造函数为公有
Object rtObject =rtConstructor.newInstance();  
Method exec = rt.getMethod("exec", String.class);  
exec.invoke(rtObject,"calc");  
}  
}

反射对类的对象的操作

对方法

Class s=Class.forName("java.lang.String");  
Method[] methods=s.getDeclaredMethods();//获取所有方法  
Method method = s.getDeclaredMethod("equals", Object.class);//获取方法  
method.setAccessible(true);//设置访问权限  
System.out.println(method.invoke(s.newInstance(),"1"));//调用方法

对属性

写一个demo类

public class demo {  
public int f=1;    
}
import java.lang.reflect.Field;  
import java.lang.reflect.InvocationTargetException;  
  
class Main{  
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {  
Class c =Class.forName("demo");  
Field[] fields = c.getDeclaredFields();//获取所有成员变量  
Field f=c.getField("f");//获取变量f1  
Object cObj=c.newInstance();  
Object fObj=f.get(cObj);//获取成员值  
System.out.println(fObj);  
f.set(cObj,114514);//修改值  
fObj=f.get(cObj);  
System.out.println(fObj);  
}  
}

同理 setAccessible可以修改访问权限

修改static final成员

demo类

public class demo {  
public static final int f=1;  
}
import java.lang.reflect.Field;  
import java.lang.reflect.InvocationTargetException;  
import java.lang.reflect.Modifier;  
  
class Main{  
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {  
Class c = Class.forName("demo");  
Field f = c.getDeclaredField("f");  
f.setAccessible(true);  
Field modifiers = f.getClass().getDeclaredField("modifiers");  
modifiers.setAccessible(true);  
modifiers.setInt(f, f.getModifiers() & ~Modifier.FINAL);  
System.out.println(f.get(null));  
f.set(null, 114514);  
System.out.println(f.get(null));  
}  
}

反射java.lang.ProcessBuilder

import java.lang.reflect.*;  
import java.util.Arrays;  
import java.util.List;  
  
class Main{  
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchFieldException {  
Class pb=Class.forName("java.lang.ProcessBuilder");  
Constructor pbConstructor =pb.getConstructor(List.class);  
Object pbObj = pbConstructor.newInstance(Arrays.asList("calc"));  
Method start=pb.getMethod("start");  
start.invoke(pbObj);  
}  
}

sun.misc.Unsafe

获取Unsafe对象

Class u = Class.forName("sun.misc.Unsafe");  
Constructor uConstructor =u.getDeclaredConstructor();  
uConstructor.setAccessible(true);  
Object uObj=uConstructor.newInstance();

对于这样一个类

import java.io.IOException;  
public class Evil {  
public Evil(){  
System.exit(0);  
}  
public void calc() throws IOException {  
Runtime.getRuntime().exec("calc");  
}  
  
}

我们没办法通过构造函数实例化一个对象,就可以利用Unsafe类的allocateInstance方法无视构造方法创建类实例

Class u = Class.forName("sun.misc.Unsafe");  
Constructor uConstructor =u.getDeclaredConstructor();  
uConstructor.setAccessible(true);  
Unsafe uObj=(Unsafe)uConstructor.newInstance();  
Evil evilObj=(Evil) uObj.allocateInstance(Evil.class);  
evilObj.calc();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值