正规的现货交易平台,seo博客网址,寿光专业做网站的公司,高端渠道开发类加载器双亲委派
什么是类加载器
类加载器是一个负责加载器类的对象#xff0c;用于实现类加载的过程中的加载这一步。每个Java类都有一个引用指向加载它的ClassLoader。而数组类是由JVM直接生成的#xff08;数组类没有对应的二进制字节流#xff09;
类加载器有哪…类加载器双亲委派
什么是类加载器
类加载器是一个负责加载器类的对象用于实现类加载的过程中的加载这一步。每个Java类都有一个引用指向加载它的ClassLoader。而数组类是由JVM直接生成的数组类没有对应的二进制字节流
类加载器有哪些
JVM 中内置了三个重要的 ClassLoader
BootstrapClassLoader启动类加载器最顶层的加载类由C实现通常表示为null并且没有父级主要用来加载JDK内部的核心类库以及被-Xbootclasspath参数指定路径下的所有类。ExtensionClassLoader扩展类加载器主要负责 “%JRE_HOME%/lib/ext” 目录下的jar包和类以及被 “java.ext.dirs” 系统变量所指定路径下的所有类。AppClassLoader应用程序类加载器面向用户的加载器负责加载应用类的path下的所有jar包和类。
还有一个是用户自定义类加载器
User ClassLoader
什么是双亲委派
当一个类加载器收到类加载器的请求的时候它不会直接加载指定的类而是把这个请求委托给自己的父加载器去加载器。只有父加载器无法加载这个类的时候才会由当前这个加载器来负责类的加载。 双亲委派的好处就是一个类名只会被一个加载器加载
类加载不同代码的加载顺序
这里的代码块主要有以下四种
静态代码块static{}构造代码块{}无参构造器ClassName()有参构造器ClassName(String name)
实例化对象
Person.java里面有静态代码块、构造代码块、无参构造器、有参构造器、静态成员变量、普通成员变量、静态方法。Main.java启动类
Person.java
package Class;public class Person {public static int statica;public int instancea;static {System.out.println(静态代码块);}Person(){System.out.println(无参构造器);}{System.out.println(构造代码块);}public Person(int instancea) {this.instancea instancea;System.out.println(有参构造 this.instancea);}public static void staticAction(){System.out.println(静态方法);}
}
Main.java
package Class;public class Main {public static void main(String[] args) throws ClassNotFoundException{Person person new Person();}
}
运行之后可以看见调用的顺序是 静态代码块-构造代码块-无参构造器
调用静态方法
调用Person的静态方法 Main.java
package Class;public class Main {public static void main(String[] args) throws ClassNotFoundException{Person.staticAction();}
}
运行Main.java后可以看见没有实例化对象会先调用 静态代码块-静态方法
对静态成员变量赋值
Main.java
package Class;public class Main {public static void main(String[] args) throws ClassNotFoundException{Person.statica 1;}
}
对静态成员变量赋值也会调用静态代码块
使用class获取类
Main.java
package Class;public class Main {public static void main(String[] args) throws ClassNotFoundException{Class c Person.class;}
}
运行后没有输出
使用forName获取类
获取类的class
需要在main方法上主动抛出异常 Main.java
package Class;public class Main {public static void main(String[] args) throws ClassNotFoundException{Class.forName(Class.Person);}
}
运行之后可以看见调用了静态代码块
有true
Main.java
package Class;public class Main {public static void main(String[] args) throws ClassNotFoundException{Class.forName(Class.Person,true,ClassLoader.getSystemClassLoader());}
}
调用静态代码块
有false
Main.java
package Class;public class Main {public static void main(String[] args) throws ClassNotFoundException{Class.forName(Class.Person,false,ClassLoader.getSystemClassLoader());}
}
可以看见没有调用任何代码块
使用ClassLoader.loadClass() 获取类
Main.java
package Class;public class Main {public static void main(String[] args) throws Exception{ClassLoader cl ClassLoader.getSystemClassLoader();Class? c cl.loadClass(Class.Person);c.newInstance();// 初始化类}
}
添加了 c.newInstance()可以看见调用了以下否则不调用
动态加载字节码
什么是字节码
Java中的字节码英文名为 “bytecode”是Java代码编译后的中间代码格式。JVMJava虚拟机执行使用的一类指令字节码通常存储在 .class文件中。
javac Hello.java这里使用 javac命令编译我们写好的java文件可以看见生成了一个 Hello.class文件这个就是字节码文件 我们打开Hello.class文件看一下可以看见都是一些看不懂的文件这些是需要JVM去做的事情才能执行 可以看见执行的时候也会输出成功因为java加载了字节码
类加载器的原理
注意
复现的时候JDK版本不可以太高否则会不一样JDK下载
https://blog.csdn.net/weixin_51959343/article/details/135921731这里主要断点调试 loadClass 这一行 这个时候就会来到它的父类抽象类 ClassLoader然后调用 loadClass()的两个参数 继续跟进来到了 AppClassLoader的loadClass() 走到下面是双亲委派的逻辑调用父类的loadClass重复查询 这个时候又回到了 ClassLoader()类中 来到所属的 Launcher类中Ctrl H 可以看见类的流程关系是 ClassLoader —- SecureClassLoader — URLClassLoader —- APPClassLoader 后面继续断点跟踪调试可以发现函数调用顺序 loadClass() - findClass() - defineClass()
任意类加载class文件
urlClassLoader
编译一个Calc类的字节码文件
package Class;import java.io.IOException;public class Calc {static {try {Runtime.getRuntime().exec(calc);} catch (IOException e) {throw new RuntimeException(e);}}
} 然后编写urlClassLoader的启动类
package Class;public class Main {public static void main(String[] args) throws Exception{URLClassLoader urlClassLoader new URLClassLoader(new URL[]{new URL(file://编译后的class文件位置路径//)});Class? c urlClassLoader.loadClass(Class.Calc); c.newInstance();}
}
运行之后可以看见成功弹出了计算器 也可以使用http协议进行远程加载
package Class;public class Main {public static void main(String[] args) throws Exception{URLClassLoader urlClassLoader new URLClassLoader(new URL[]{new URL(http://localhost:8080)});Class? c urlClassLoader.loadClass(Class.Calc); c.newInstance();}
}
在字节码文件目录下开启http服务 运行之后可以看见弹出计算器 也可以使用jar
前面 jar:后面 xxx.jar!/
package Class;public class Main {public static void main(String[] args) throws Exception{URLClassLoader urlClassLoader new URLClassLoader(new URL[]{new URL(jar:http://localhost:8080/Calc.jar!/)});Class? c urlClassLoader.loadClass(Class.Calc);c.newInstance();}
}file协议同理
defineClass
也可以直接使用 defineClass方法加载恶意的class文件
package Class;public class Main {public static void main(String[] args) throws Exception{//获取ClassLoader类的系统类加载器赋值到c1变量ClassLoader cl ClassLoader.getSystemClassLoader();//使用反射机制获取 ClassLoader 类的 defineClass 方法中的一些一些参数Method defineClassCalc ClassLoader.class.getDeclaredMethod(defineClass, String.class, byte[].class, int.class, int.class);//设置权限为可访问defineClassCalc.setAccessible(true);//byte[]数组存储了这个目录下的Calc.class字节码文件byte[] code Files.readAllBytes(Paths.get((E:\\语言学习\\java\\code\\Serialize1\\Serialize1\\out\\production\\Serialize1\\Class\\Calc.class)));//反射调用了之前获取到的 defineClass 方法将字节码数组转换为 Class 对象赋值给cClass c (Class) defineClassCalc.invoke(cl,Class.Calc,code,0,code.length);//初始化 Class对象 cc.newInstance();}
} 缺点就是 defineClass方法是protected受保护的成员在反序列化中难以反射调用
Unsafe
Unsafe 方法是采用单例模式进行设计的无法直接调用所以需要反射
package Class;public class Main {public static void main(String[] args) throws Exception{//获取ClassLoader类的系统类加载器赋值到c1变量ClassLoader cl ClassLoader.getSystemClassLoader();//获取 Unsafe的类ClassUnsafe unsafeClass Unsafe.class;//通过反射 获取 Unsafe类的 theUnsafe字段Field theUnsafeField unsafeClass.getDeclaredField(theUnsafe);//设置访问权限为truetheUnsafe是prvate成员theUnsafeField.setAccessible(true);byte[] code Files.readAllBytes(Paths.get((E:\\语言学习\\java\\code\\Serialize1\\Serialize1\\out\\production\\Serialize1\\Class\\Calc.class)));//获取 Unsafe对象theUnsafeField方法静态的字段的值Unsafe unsafe (Unsafe) theUnsafeField.get(null);Class c2 unsafe.defineClass(Class.Calc, code, 0, code.length, cl, null);c2.newInstance();}
} 总结
总的来说就是加载器字节码之间怎么构造一条链子底层之间产生的漏洞