自定义classloader实现java程序加密

自定义classloader实现java程序加密

背景
项目使用java语言开发,发布包会整合到C++客户端,直接安装到用户机器上,不同于之前常见的后端服务发布,这种发布能让用户直接接触到java发布包,由于java语言的特性,有可能出现java发布包被反编译的情况,所以决定对java发布包进行加密处理。
ClassLoader概述
java编译后的class文件由classloader(类加载器)加载,并生成class定义(java对象类型),虚拟机每次需要访问到对象类型时,操作包括不限于对象实例化,对象类型查看,序列化等这些,都会访问classloader。虚拟机自带了3个类加载器:启动类加载器,扩展类加载器,应用程序类加载器。这3个类加载器都继承了ClassLoader类,如果开发人员需要自定义自己的类加载器,只需要继承ClassLoader类,覆写loadClass或者findClass即可。
ClassLoader研究
虚拟机自带ClassLoader:
1. 启动类加载器:由c++编写,作为程序所有类加载器的最顶级父类加载器。
2. 扩展类加载器:继承于启动类加载器,由java语言编写,对启动类加载器做了一些拓展,与这次项目无关,所以不做过多研究。
3. 应用程序类加载器:继承于扩展类加载器,最接近开发人员的类加载器,又称为系统类加载器,默认情况下,通过当前线程获取到的上下文类加载器就是这个。如果开发人员自定义了类加载器,一般会以这个类加载器为父类加载器。当前线程可以设置自己的上下文类加载器,设置了之后,仅仅作用于当前线程和该线程子线程,应用程序类加载器不会被覆盖,可以通过自定义类加载器或者获取系统类加载器方法去访问应用程序类加载器,但是不允许对应用程序类加载器进行改动。
注:从以上类加载器继承关系可以看出,类加载器访问顺序采用递归方式,就是常说的双亲委托,访问顺序从最顶级类加载器开始,如果找不到就从它的子类找,向下遍历直到当前类加载器结束,中途如果找到了就提前结束遍历。
自定义ClassLoader:
开发人员如果需要自定义自己的类加载器,只需要继承ClassLoader类,覆写loadClass或者findClass即可。
覆写findClass就可以实现定义对象类型功能,但并不能改变类加载器访问顺序,我们自定义了ClassLoader之后,希望它能像maven仓库查找jar那种,先从最靠近我们的“仓库”开始查找,找不到才访问它的父类加载器,这样明显提升了查找效率,但会破坏java自带的双亲委托访问方式。
加密方案
class文件加密采用AES算法,这个加密算法可替换。
加密流程主要是将jar的里class文件读取为二级制格式(byte[]),使用AES算法对字节数组内容进行加密,然后创建一个文件后缀名不为.class的文件(例如后缀名为.ext),然后将加密后的字节数组内容写入到.ext文件里,这样做是为了避开java虚拟机自动使用自带类加载器加载class文件,造成类加载异常。
解密流程主要是在自定义ClassLoader的findClass里读取.ext文件内容为字节数组,然后进行加密,然后使用当前ClassLoader的defineClass方法对该类进行定义。(注:defineClass方法可以通过传入类名和字节数组定义一个对象类型,定义后这个对象类型会被当前ClassLoader缓存,且该对象类型被绑定到当前ClassLoader,作为当前ClassLoader私有资源,如果在其它ClassLoader访问此对象类型,会抛出类找不到异常)
spring兼容
由于加密后文件采用了.ext作为后缀名,所以如果程序里有通过文件路径直接查找.class文件的操作,肯定会出现找不到文件异常,例如,spring通过注解扫描去初始化bean时候,会通过类路径加上.class作为文件名去查找class文件,这时候我们jar里仅有.ext文件,但是可以通过覆写查找class文件方法,然后进行数据转换去返回跟原本.class文件一样的内容。spring查找.class文件还会有判断该文件是否存在和可读的方法,这些都要覆写,否则会导致spring查找.class文件提前返回,然后导致bean初始化不成功。
ClassLoader适配
前面说过当前线程会有自己的上下文类加载器,这个类加载器会对当前线程和它的子线程有效,然后自定义类加载器绑定到了当前线程,但是如果调用了Class.forName(),虚拟机并不会从当前线程绑定的类加载器开始查找,而是从当前这个类的类加载器开始查找,看了Class.forName()的源代码就能确认这个加载机制,所以自定义ClassLoader所定义的对象类型,如果想让在其它类调用Class.forName()能找到类,有两个方法,一个是指定类加载器,一个是将调用源的那个类加载到自定义类加载器里,这样一个类就会有两个类加载器对它进行了定义,因为自定义类加载器已经绑定到了当前线程,所以在查找类定义时会从自定义类加载器开始查找,由于我们已经覆写了loadClass方法,重新设置了类查找顺序,如果在当前类加载器找到就会返回,不会有类定义冲突这种情况。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值