Java代码审计——ClassLoader 类加载机制

news2024/11/24 3:12:55

目录

前言:

(一)类加载机制

0x01 ClassLoader 类

0x02 loadClass()方法的流程

0x03  自定义的类加载器

0x04 loadClass()方法与 Class.forName 的区别

0x05 URLClassLoader

(二)Java 动态代理

0x01 静态代理

 0x02 动态代理

 1.Proxy 类

2.InvocationHandler 接口

0x03 CGLib 代理

(三)Javassist 动态编程


前言:

        Java 程序是由 class 文件组成的一个完整的应用程序。在程序运行时,并不会一 次性加载所有的 class 文件进入内存,而是通过 Java 的类加载机制( ClassLoader )进 行动态加载,从而转换成 java.lang.Class 类的一个实例。

(一)类加载机制


0x01 ClassLoader 类


        ClassLoader 是一个抽象类,主要的功能是通过指定的类的名称,找到或生成对 应的字节码,返回一个 java.lang.Class 类的实例。开发者可以继承 ClassLoader 类来 实现自定义的类加载器。
        ClassLoader 类中和加载类相关的方法如图 1-1 所示
图 1-1 ClassLoader 类中和加载类相关的方法

0x02 loadClass()方法的流程


        前面曾介绍过 loadClass() 方法可以加载类并返回一个 java.lang.Class 类对象。通 过如下源码可以看出,当 loadClass() 方法被调用时,会首先使用 findLoadedClass() 法判断该类是否已经被加载,如果未被加载,则优先使用加载器的父类加载器进行 加载。当不存在父类加载器,无法对该类进行加载时,则会调用自身的 findClass() 方法,可以重写 findClass() 方法来完成一些类加载的特殊要求。该方法的代码如 下所示:
protected Class<?> loadClass(String name, boolean resolve) 
 throws ClassNotFoundException 
     { 
             synchronized (getClassLoadingLock(name)) { 
             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) { 
     //省略
             c = findClass(name); 
     //省略
         } 
     } 
         if (resolve) { 
             resolveClass(c); 
         } 
     return c; 
     } 
 }

0x03  自定义的类加载器


        根据loadClass() 方法的流程,可以发现通过重写 findClass() 方法,利用 defineClass() 方法来将字节码转换成  java.lang.class  类对象,就可以实现自定义的类加载器。示例 代码如下所示:
public class DemoClassLoader extends ClassLoader { 
     private byte[] bytes ; 
     private String name = ""; 
     public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, In
        stantiationException { 
             String clzzName = "com.test.Hello"; 
             byte[] testBytes = new byte[]{ 
 -54, -2, -70, -66, 0, 0, 0, 52, 0, 28, 10, 0, 6, 0, 14, 9, 
0, 15, 0, 16, 8, 0, 17, 10, 0, 18, 0, 19, 7, 
 0, 20, 7, 0, 21, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0,
3, 40, 
 //省略
}; 
         DemoClassLoader demo = new DemoClassLoader(clzzName,testBytes); 
         Class clazz = demo.loadClass(clzzName); 
         Constructor constructor = clazz.getConstructor(); 
         Object obj = constructor.newInstance(); 
         Method method = clazz.getMethod("sayHello"); 
         method.invoke(obj); 
 } 
 public DemoClassLoader(String name, byte[] bytes){ 
         this.name = name;
         this.bytes = bytes; 
     } 
     @Override 
     protected Class<?> findClass(String name) throws ClassNotFoundException
     { 
         if(name.equals(this.name)) { 
             defineClass(name, bytes, 0, bytes.length); 
         } 
         return super.findClass(name); 
         } 
}
  • 该示例代码的执行结果如图 3-1 所示:
图 3-1 define过滤器的执行结果

0x04 loadClass()方法与 Class.forName 的区别


        loadClass()方法只对类进行加载,不会对类进行初始化。 Class.forName 会默认对 类进行初始化。当对类进行初始化时,静态的代码块就会得到执行,而代码块和构 造函数则需要适合的类实例化才能得到执行,示例代码如下所示:
public class Dog { 
     static { 
         System.out.println("静态代码块执行");
        } 
     { 
         System.out.println("代码块执行"); 
 } 
     public Dog(){ 
         System.out.println("构造方法执行"); 
             } 
    } 

public class ClassLoaderTest { 
         public static void main(String[] args) throws ClassNotFoundException { 
                 Class.forName("Dog"); 
                 ClassLoader.getSystemClassLoader().loadClass("Dog"); 
         } 
}
  • 该示例代码的执行结果如图 4-1 所示
图 4-1 静态代码执行结果

 

0x05 URLClassLoader


URLClassLoader 类是 ClassLoader 的一个实现,拥有从远程服务器上加载类的能
力。通过 URLClassLoader 可以实现对一些 WebShell 的远程加载、对某个漏洞的深入
利用。

(二)Java 动态代理


0x01 静态代理


        所谓静态代理,顾名思义,当确定代理对象和被代理对象后,就无法再去代理 另一个对象。同理,在 Java 静态代理中,如果我们想要实现另一个代理,就需要重 新写一个代理对象,其原理如图 5-1 所示
图 5-1 静态代理的原理

 

        总而言之,在静态代理中,代理类和被代理类实现了同样的接口,代理类同时 持有被代理类的引用。当我们需要调用被代理类的方法时,可以通过调用代理类 方法实现,静态代理的实现如图 5-2 所示:
图 5-2 静态代理的实现

 0x02 动态代理


        静态代理的优势很明显,即允许开发人员在不修改已有代码的前提下完成一些 增强功能的需求。但是静态代理的缺点也很明显,它的使用会由于代理对象要实现 与目标对象一致的接口,从而产生过多的代理类,造成冗余;其次,大量使用静态 代理会使项目不易维护,一旦接口增加方法,目标对象与代理对象就要进行修改。 而动态代理的优势在于可以很方便地对代理类的函数进行统一的处理,而不用修改 每个代理类中的方法。对于我们信息安全人员来说,动态代理意味着什么呢?
        实际 上, Java 中的“动态”也就意味着使用了反射,因此动态代理其实是基于反射机制 的一种代理模式。
        如图 5-3 所示,动态代理与静态代理的区别在于,通过动态代理可以实现多个需 求。动态代理其实是通过实现接口的方式来实现代理,具体来说,动态代理是通过 Proxy 类创建代理对象,然后将接口方法“代理”给 InvocationHandler 接口完成的。
图 5-3 动态代理的实现

        动态代理的关键有两个,即上文中提到的 Proxy 类以及 InvocationHandler 接口, 这是我们实现动态代理的核心。

 1.Proxy 类


  在 JDK 中, Java 提供了
  1. Java.lang.reflect.InvocationHandler 接口
  2. Java.lang.reflect.Proxy
这两个类相互配合,其中 Proxy 类是入口。 Proxy 类是用来创建一 个代理对象的类,它提供了很多方法:
  • static Invocation Handler get Invocation Handler (Object proxy) :该方法主要用于获取指定代理对象所关联的调用程序
  • static Class<?> get Proxy Class (ClassLoader loader, Class<?>... interfaces) :该方法主要用于返回指定接口的代理类
  • static Object newProxyInstance (ClassLoader loader, Class<?>[] interfaces, Invocation Handler h):该方法主要返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序。
  • static boolean is Proxy Class (Class<?> cl):当且仅当指定的类通过 get ProxyClass 方法或 newProxyInstance 方法动态生成为代理类时,返回 true。该方法的可靠性对于使用它做出安全决策而言非常重要,所以它的实现不应仅测试相关的类是否可以扩展 Proxy

        在上述方法中,最常用的是 newProxyInstance 方法,该方法的作用是创建一个代 理类对象,它接收 3 个参数: loader interfaces 以及 h ,各个参数含义如下。
  • loader:这是一个 ClassLoader 对象,定义了由哪个 ClassLoader 对象对生成的代理类进行加载。
  • interfaces:这是代理类要实现的接口列表,表示用户将要给代理对象提供的接口信息。如果提供了这样一个接口对象数组,就是声明代理类实现了这些接口,代理类即可调用接口中声明的所有方法。
  • h:这是指派方法调用的调用处理程序,是一个 InvocationHandler 对象,表示当动态代理对象调用方法时会关联到哪一个 InvocationHandler 对象上,并最终由其调用。

2.InvocationHandler 接口


        Java.lang.reflect InvocationHandler,主要方法为 Object invoke Object proxy,
Method method, Object[] args ),该方法定义了代理对象调用方法时希望执行的动作, 用于集中处理在动态代理类对象上的方法调用。 Invoke 3 个参数: proxy method args ,各个参数含义如下:
  • proxy:在其上调用方法的代理实例。 
  • method:对应于在代理实例上调用的接口方法的 Method 实例 Method 象的声明类将是在其中声明方法的接口,该接口可以是代理类赖以继承方法的代理接口的超接口。
  • args:包含传入代理实例上方法调用的参数值的对象数组,如果接口方法不使用参数,则为 null。基本类型的参数被包装在适当基本包装器类(如 Java.lang.Integer Java.lang.Boolean)的实例中。

0x03 CGLib 代理


        CGLib( Code Generation Library )是一个第三方代码生成类库,运行时在内存中 动态生成一个子类对象,从而实现对目标对象功能的扩展。动态代理是基于 Java 射机制实现的,必须实现接口的业务类才能使用这种办法生成代理对象。而 CGLib 则基于 ASM 机制实现,通过生成业务类的子类作为代理类。 与动态代理相比,动态代理只能基于接口设计,对于没有接口的情况, JDK 式无法解决,而 CGLib 则可以解决这一问题;其次, CGLib 采用了非常底层的字节 码技术,性能表现也很不错。

(三)Javassist 动态编程


        
        在了解 Javassist 动态编程之前,首先来了解一下什么是动态编程。动态编程是 相对于静态编程而言的一种编程形式,对于静态编程而言,类型检查是在编译时完 成的,但是对于动态编程来说,类型检查是在运行时完成的。因此所谓动态编程就 是绕过编译过程在运行时进行操作的技术。
        那么动态编程可以解决什么样的问题呢?其实动态编程做的事情,静态编程也 可以做到,但相对于动态编程来说,静态编程要实现动态编程所实现的功能,过程 会比较复杂。一般来说,在依赖关系需要动态确认或者需要在运行时动态插入代码 的环境中,需要使用动态编程。
        Java 字节码以二进制形式存储在 class 文件中,每一个 class 文件都包含一个 Java 类或接口。 Javassist 就是一个用来处理 Java 字节码的类库,其主要优点在于简 单、便捷。用户不需要了解虚拟机指令,就可以直接使用 Java 编码的形式,并且可 以动态改变类的结构,或者动态生成类。

        Javassist 中最为重要的是 ClassPoolCtClass CtMethod 以及 CtField 4 个类:

  • ClassPool:一个基于 HashMap 实现的 CtClass 对象容器,其中键是类名称,值是表示该类的 CtClass 对象。默认的 ClassPool 使用与底层 JVM 相同的类路径,因此在某些情况下,可能需要向 ClassPool 添加类路径或类字节。
  • CtClass:表示一个类,这些 CtClass 对象可以从 ClassPool 获得。
  • CtMethods:表示类中的方法。
  • CtFields:表示类中的字段。

Javassist 官方文档中给出的代码示例如下:

ClassPool pool = ClassPool.getDefault(); 
CtClass cc = pool.get("test.Rectangle"); 
cc.setSuperclass(pool.get("test.Point"));
cc.writeFile();
        这段程序首先获取 ClassPool 的实例,它主要用来修改字节码,里面存储着基于 二进制文件构建的 CtClass 对象,它能够按需创建出 CtClass 对象并提供给后续处理 流程使用。当需要进行类修改操作时,用户需要通过 ClassPool 实例的 .get() 方法获取 CtClass 对象。
        我们可以从上面的代码中看出,ClassPool getDefault() 方法将会查找系统默认 的路径来搜索 test.Rectable 对象,然后将获取到的 CtClass 对象赋值给 cc 变量。 这里仅是构造 ClassPool 对象以及获取 CTclass 的过程,具体的 Javassist 的使用 流程如图 3-1 所示。

图 3-1  Javassist 的使用流程

 

        操作 Java 字节码有两个比较流行的工具,即 Javassist ASM Javassist 的优点 是提供了更高级的 API,无须掌握字节码指令的知识,对使用者要求较低,但同时其 执行效率相对较差; ASM 则直接操作字节码指令,执行效率高,但要求使用者掌握 Java 类字节码文件格式及指令,对使用者的要求比较高。
        安全人员能够利用 Javassist 对目标函数动态注入字节码代码。通过这种方式, 我们可以劫持框架的关键函数,对中间件的安全进行测试,也可以劫持函数进行攻 击阻断。此外,对于一些语言也可以很好地进行灰黑盒测试。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/58521.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022最新 MySQL 内部技术架构面试题

更多面试题&#xff1a;https://javaxiaobear.gitee.io/ 47、MySQL内部支持缓存查询吗&#xff1f; 当MySQL接收到客户端的查询SQL之后&#xff0c;仅仅只需要对其进行相应的权限验证之后&#xff0c;就会通过Query Cache来查找结果&#xff0c;甚至都不需要经过Optimizer模…

dot product【点积】

&#xff08;1&#xff09;概念 点积在数学中&#xff0c;又称数量积&#xff08;dot product; scalar product&#xff09;&#xff0c;是指接受在实数R上的两个向量并返回一个实数值标量的二元运算。 两个向量a [a1, a2,…, an]和b [b1, b2,…, bn]的点积定义为&#xff…

点云数据集ShapeNet

目录 1. 数据采集的作者 2. 下载shapenet数据集的代码 3. 数据特点 3.1 每个数字文件夹代表一个类别 3.2 synsetoffset2category.txt 3.3 train_test_split文件夹 3.4 pts点云文件 3.6 seg分割类别文件 1. 数据采集的作者 article{yi2016scalable,title{A scalable ac…

idea导入eclipse项目的时候,Java图标变成黄色小J了,怎么解决?

凯哥今天导入一个15年时候写的小项目&#xff0c;当时使用的是eclipse写的。最近好几年都在使用idea&#xff0c;习惯了idea的&#xff0c;在用eclipse&#xff0c;不习惯&#xff0c;不顺手&#xff0c;就导入到idea中。发现&#xff0c;Java文件的图标变成了黄色的J。如下图&…

【Linux】详解套接字编程

文章目录网络套接字1.端口号1.1认识端口号1.2端口号VS PID2.TCP与UDP协议3.网络字节序4.socket编程4.1常用接口4.2sockaddr结构4.3.socket接口的底层工作4.4字符串IP VS 整形IP4.5 bind与INADDR_ANY5.UDP聊天服务器5.1va_start和va_end5.2vsnprintf函数5.3自定义日志类5.4UDP服…

【RTS】杜金房大神FreeSwitch分享笔记

技术万变不离其宗不管如何实现原理都是一样的。杜金房大神 RTS 高可用 一台机器上俩fs,公用同一个ip用户连接的是一个ip,不知道切了fs。两台主备数据同步

ARM 汇编编写 LED 灯

一、一步步点亮LED 1. 硬件工作原理及原理图查阅 LED 本身有 2 个接线点&#xff0c;一个是 LED 的正极&#xff0c;一个是 LED 的负极。LED 这个硬件的功能就是点亮或者不亮&#xff0c;物理上想要点亮一颗 LED 只需要给他的正负极上加正电压即可&#xff0c;要熄灭一颗 LED…

Linpack安装测试流程记录

软件背景 虽然很早就接触了HPC&#xff0c;也参与过一些项目&#xff0c;诸如电影动画渲染集群以及某博导老师的基因分析计算集群&#xff0c;但是对于跑超算的linpack&#xff0c;一直没时间上手玩。 Linpack是超算必测项目,也是考验优化能力的套件,很有意思&#xff0c;记录…

软件测试工程师到底要不要转行开发? 2022测试生涯该如何转型升级?

测试工程师到底是干啥的&#xff1f; 测试工程师转开发有多大希望&#xff1f; 为了能够解除大家心中的疑惑&#xff0c;我决定从以下几个方面来补充回答&#xff1a; 测试工程师到底是干什么的&#xff1f; 测试工程师转开发有多大希望&#xff1f; 测试工程师一定要转开发吗…

2023秋招,Java岗最全面试攻略,吃透25个技术栈Offer拿到手软!

我分享的这份春招 Java 后端开发面试总结包含了 JavaOOP、Java 集合容器、Java 异常、并发编程、Java 反射、Java 序列化、JVM、Redis、Spring MVC、MyBatis、MySQL 数据库、消息中间件 MQ、Dubbo、Linux、ZooKeeper、 分布式 &数据结构与算法等 25 个专题技术点&#xff0…

腾讯云服务器2核4G、4核8G、8核16G、16核32G配置报价表出炉

现在腾讯云服务器2核2G、2核4G、4核8G、8核16G、16核32G配置价格表已经出来了&#xff0c;大家可以参考一下。腾讯云轻量应用服务器为轻量级的云服务器&#xff0c;使用门槛低&#xff0c;按套餐形式购买&#xff0c;轻量应用服务器套餐自带的公网带宽较大&#xff0c;4M、6M、…

rocketmq安装、启动

1、下载 >wget http://mirror.bit.edu.cn/apache/rocketmq/4.4.0/rocketmq-all-4.4.0-source-release.zip >unzip rocketmq-all-4.4.0-source-release.zip > cd rocketmq-all-4.4.0/ > mvn -Prelease-all -DskipTests clean install -U > cd distribution/targ…

[附源码]计算机毕业设计学生宿舍管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

5.函数与递归

一、函数 1.基本介绍 此前我们使用了很多库函数&#xff0c;现在我们可以定义自己的函数来帮助我们完成一些特定的任务。 函数返回值类型 函数名(变量1,变量2,...,变量n) {...return; }函数返回值类型有很多类&#xff1a; 可以为char,int,double,long long,string等基础数…

【servelt原理_13_状态管理】

状态管理 1.现有问题 Http是无状态的&#xff0c;不能保存每次提交的信息如果用户发来一个新的请求&#xff0c;服务器无法知道它是否与上次请求是否有联系.对于那么需要提交多次信息才能完成的操作&#xff0c;比如购物&#xff0c;就很有问题 2.概念 将浏览器和web服务器之…

npm vue 路由之一级路由(npm默认已经集成了vue)

npm vue 路由之一级路由&#xff08;npm默认已经集成了vue&#xff09; 文档https://v3.router.vuejs.org/zh/installation.html npm install vue-router3.5.2 --save 1.在App.vue上面添加 <router-view></router-view>2.在main.js上面添加 import VueRouter fro…

【计算机网络】计算机网络复习总结 ----- 计算机网络概述

计算机网络 内容管理计算机网络概述计算机网络定义计算机网络、互联网、因特网计算机网络产生和发展 &#xff08;略&#xff09;计算机网络分类按照网络作用范围分类&#xff1a;按照拓扑结构分类&#xff1a;按照交换技术分类按照应用模式分类&#xff1a;按照工作方式分类相…

opencv c++ 轮廓匹配

1、几何矩和Hu矩 1.1几何矩 a&#xff09;几何计算公式&#xff1a; p、q为阶数&#xff0c;当pq 1时&#xff0c;几何矩为一阶矩&#xff0c;pq 2&#xff0c;几何矩为二阶矩&#xff0c;依次类推。。 因此&#xff0c;对于二值图像有&#xff1a; 所有前景像素的x坐标之…

Spring对AOP的实现

Spring对AOP实现的模式分为2种&#xff0c;一种是代理&#xff0c;一种是AspectJ&#xff0c;这种区分方式是直接使用实现方式区分的。 二、Spring对动态代理的设计 动态代理我们都知道在Spring中分为JDK动态代理和cglib动态代理&#xff0c;JDK动态代理自不用说&#xff0c;…