自定义解密类加载器

概述

我们平常写好的java代码并不是直接就能给计算机使用,而是需要通过编译成.class字节码文件,通过类加载器加载到jvm中。

1.字节码类文件

假如我们有如下的java代码段,我们应该怎么让jvm运行起来呢?

        int i=1;
        int j=2;
        i=i+j;

通过把java文件编译成字节码类文件(.class)后,就可以被jvm快速识别并加载。

从上图可能我们可能看不懂里面的意思,我们通过javap -c 来编译对应的class文件,我们看到字节码对应的助记符,如下,其实class文件就以我们编写的java文件逻辑为基础,编译成jvm能识别运行的字节码。

2.什么是类加载器

虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类。实现这个动作的代码模块称为“类加载器”。

2.1 默认加载器

As of JDK 1.2, a bootstrap class loader that is built into the JVM is responsible for loading the classes of the Java runtime. This class loader only loads classes that are found in the boot classpath, and since these are trusted classes, the validation process is not performed as for untrusted classes. In addition to the bootstrap class loader, the JVM has an extension class loader responsible for loading classes from standard extension APIs, and a system class loader that loads classes from a general class path as well as your application classes.。

jvm提供了3个默认的加载器指定去加载对应的类(BootstrapClassLoader/ExtClassLoader/AppClassLoader)

BootstrapClassLoader:jvm自身需要用到的class

ExtClassLoader:官方提供工具类class

AppClassLoader:应用自身class及其他依赖 

2.2 类加载器委托模式(双亲委托模式)

除了顶级的BootstrapClassLoader以外,其他加载器都必须要有自己的父级加载器,如图所示:

即优先判断判断父级加载器是否能加载该类,当父亲加载器加载失败的时候,才自己进行类加载,从而达到不重复加载。其次,双亲委托模式的概念使jvm核心的类不会被外部加载替换。

3.自定义类加载器

jvm加载class的规范是统一的,因此加载class文件内容必须满足jvm对class文件的内容规范,否则jvm将无法加载,因此如果我们修改过的class文件在常规的类加载器进行加载会失败,因此在给class加密之后需要自定义对应对应的加载器进行解密,然后再进行加载。

已知源码为:

public class SayHello {
    public SayHello()
    {
        System.out.println("This is SayHello Class");
    }
}

3.1 凯撒加密

凯撒密码(英语:Caesar cipher),或称凯撒加密、凯撒变换、变换加密,是一种最简单且最广为人知的加密技术。凯撒密码是一种替换加密技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。例如,当偏移量是3的时候,所有的字母A将被替换成D,B变成E,以此类推。这个加密方法是以罗马共和时期凯撒的名字命名的,据称当年凯撒曾用此方法与其将军们进行联系。

通过给原字节码文件每个字节+5个偏移量来对字节码文件加密,保存为新的字节码文件,反编译EncryptSayHello.class将无法编译出其逻辑。

        File file=new File("/Users/kirra/Desktop/project/javabasic/ByteCodeEncrypt/src/SayHello.class");
        byte[] bytesArray = new byte[(int) file.length()];
        FileInputStream fis = new FileInputStream(file);
        fis.read(bytesArray); //read file into bytes[]
        fis.close();
        for(int i=0;i<bytesArray.length;i++)
        {
            bytesArray[i]= (byte) (bytesArray[i]+5);
        }
        File kenhofile=new File("/Users/kirra/Desktop/project/javabasic/ByteCodeEncrypt/src/EncryptSayHello.class");
        if(!kenhofile.exists())
            kenhofile.createNewFile();
        FileOutputStream fileOutputStream=new FileOutputStream(kenhofile);
        fileOutputStream.write(bytesArray);
        fileOutputStream.close();

3.2 自定义解码类加载器

通过继承classLoader然后对应findClass方法进行重写,因为在加密的时候对每个字节码进行了5个平移量,那么在自定义的类加载器中要把这5个平移量替换回来。

 protected Class<?> findClass(String name) throws ClassNotFoundException {
        File file=new File("/Users/kirra/Desktop/project/javabasic/ByteCodeEncrypt/src/Encrypt"+name+".class");
        byte[] bytes=new byte[(int)file.length()];
        try {
            FileInputStream fileInputStream=new FileInputStream(file);
            fileInputStream.read(bytes);
            fileInputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        for(int i=0;i<bytes.length;i++)
            bytes[i]= (byte) (bytes[i]-5);
        return defineClass(name, bytes, 0, bytes.length);
    }

github:https://github.com/tale2009/Java-Basic-Lab/tree/main/ByteCodeEncrypt/src

参考:https://www.oracle.com/technical-resources/articles/javase/classloaders.html

https://zh.wikipedia.org/wiki/%E5%87%B1%E6%92%92%E5%AF%86%E7%A2%BC

https://www.zhihu.com/question/46719811?sort=created

《深入理解Java虚拟机》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值