首页 > 系统相关 >一文深度学习java内存马

一文深度学习java内存马

时间:2024-10-25 12:19:05浏览次数:3  
标签:java 一文 对象 class 内存 字节 Class 加载

就是要打骨折icon-default.png?t=O83Ahttp://mp.weixin.qq.com/s?__biz=MzkwNjY1Mzc0Nw==&mid=2247486065&idx=2&sn=b30ade8200e842743339d428f414475e&chksm=c0e4732df793fa3bf39a6eab17cc0ed0fca5f0e4c979ce64bd112762def9ee7cf0112a7e76af&scene=21#wechat_redirect

《Java代码审计》icon-default.png?t=O83Ahttp://mp.weixin.qq.com/s?__biz=MzkwNjY1Mzc0Nw==&mid=2247484219&idx=1&sn=73564e316a4c9794019f15dd6b3ba9f6&chksm=c0e47a67f793f371e9f6a4fbc06e7929cb1480b7320fae34c32563307df3a28aca49d1a4addd&scene=21#wechat_redirect

《Web安全》icon-default.png?t=O83Ahttp://mp.weixin.qq.com/s?__biz=MzkwNjY1Mzc0Nw==&mid=2247484238&idx=1&sn=ca66551c31e37b8d726f151265fc9211&chksm=c0e47a12f793f3049fefde6e9ebe9ec4e2c7626b8594511bd314783719c216bd9929962a71e6&scene=21#wechat_redirect

1. 前言

java内存马注入一般分为两种

  • 动态注册添加新的listener/filter/servlet/controller等等
  • agent注入修改已有class,插入恶意代码


在理解内存马注入前,有几个概念需要掌握的。

  • 类加载
  • 双亲委派问题以及context
  • 类反射

2. 基础

2.1. class对象

java中的对象可以分为两种对象:Class对象和实例对象

  1. 信息属性:从对象的作用看,Class对象保存每个类型运行时的类型信息,如类
    名、属性、方法、父类信息等等。在JVM中,一个类只对应一个Class对象
  2. 普适性:Class对象是java.lang.Class类的对象,和其他对象一样,我们可以获取
    并操作它的引用
  3. 运行时唯一性:每当JVM加载一个类就产生对应的Class对象,保存在堆区,类
    型和它的Class对象时一一对应的关系。一旦类被加载了到了内存中,那么不论通过哪种
    方式获得该类的Class对象,它们返回的都是指向同一个java堆地址上的Class对象引用。
    JVM不会创建两个相同类型的Class对象

Class类没有公共的构造方法,Class对象是在类加载的时候由Java虚拟机以及通过
调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。

2.2.类加载

一个类被加载到内存并供我们使用需要经历如下三个阶段:

  • 加载,这是由类加载器(ClassLoader)执行 的。通过一个类的全限定名来获
    取其定义的二进制字节流(Class字节码),将这个字节流所代表的静态存储结
    构转化为方法去的运行时数据接口,根据字节码在java堆中生成一个代表这个类
    的java.lang.Class对象
  • 链接。在链接阶段将验证Class文件中的字节流包含的信息是否符合当前虚拟机
    的要求,为静态域分配存储空间并设置类变量的初始值(默认的零值),并且
    如果必需的话,将常量池中的符号引用转化为直接引用。
  • 初始化。到了此阶段,才真正开始执行类中定义的java程序代码。用于执行该
    类的静态初始器和静态初始块,如果该类有父类的话,则优先对其父类进行
    初始化

所有的类都是在对其第一次使用时,动态加载到JVM中的(懒加载
触发类加载的方式

  • Class.forName("类的全限定名")
  • new 类构造方法

除了上述方式,还可以通过网络加载字节码方式来调用外部类

  • oadclass:判断是否已加载,使用双亲委派模型,请求父加载器,都为空,使用
    findclass
  • findclass:根据名称或位置加载.class字节码,然后使用defineClass
  • defineclass:解析定义.class字节流,返回class对象
  • loadClass 的作用是从已加载的类缓存、父加载器等位置寻找类(这里实际上是
    双亲委派机制),在前面没有找到的情况下,执行 findClass
  • findClass 的作用是根据基础URL指定的方式来加载类的字节码,就像上一节中
    说到的,可能会在本地文件系统、jar包或远程http服务器上读取字节码,然后交
    给 defineClass
  • defineClass 的作用是处理前面传入的字节码,将其处理成真正的Java类

所以可见,真正核心的部分其实是 defineClass ,他决定了如何将一段字节流转变成
一个Java类,Java默认的 ClassLoader#defineClass 是一个native方法,逻辑在JVM的C语言
代码中

classloader推荐

jxxload_help.PathVFSJavaLoader#loadClassFromBytes
org.python.core.BytecodeLoader1#loadClassFromBytes
sun.org.mozilla.javascript.internal.DefiningClassLoader#defineCl
ass
java.security.SecureClassLoader#defineClass(java.lang.String,
byte[], int, int, java.security.CodeSource)
org.mozilla.classfile.DefiningClassLoader#defineClass
org.mozilla.javascript.DefiningClassLoader
com.sun.org.apache.bcel.internal.util.ClassLoader
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl

类装载器是用来把类(class)装载进 JVM 的。JVM 规范定义了两种类型的类装载器:
启动类装载器(bootstrap)和用户自定义装载器(user-defined class loader)。 JVM在运行时会
产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:

//1、获取系统类的加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
//2. 获取系统类加载器的父类加载器(扩展类加载器,可以获取).
classLoader = classLoader.getParent();
System.out.println(classLoader);
//3. 获取扩展类加载器的父类加载器(引导类加载器,不可获取).
classLoader = classLoader.getParent();
System.out.println(classLoader);

2.3. 类反射

其实就是获取Class对象,然后调用该对象进行一系列操作

  • Class: 是一个类; 一个描述类的类
  • 封装了描述方法的 Method
  • 描述字段的 Filed
  • 描述构造器的 Constructor 等属性

如何得到 Class 对象

  1. Person.class
  2. person.getClass()
  3. Class.forName("com.atguigu.javase.Person")

关于 Method

如何获取 Method:

  1. getDeclaredMethods: 得到 Method 的数组
  2. getDeclaredMethod(String methondName, Class ...
    parameterTypes) 可以拿到反射类中的公共方法、私有方法、保
    护方法、默认访问,但不获得父类方法
  3. getMethod(String methondName, Class ... parameterTypes)可以
    拿到反射类及其父类中的所有公共方法, 但无法获取私有方法

如何调用 Method

  1. 如果方法时 private 修饰的, 需要先调用 Method 的
    setAccessible(true), 使其变为可访问
  2. method.invoke(obj, Object ... args)

关于 Field

如何获取 Field: getField(String fieldName)

如何获取 Field 的值

  1. setAccessible(true)
  2. field.get(Object obj)

如何设置 Field 的值

  1. field.set(Obejct obj, Object val)

如果属性用 final 修饰的,需要获取Field的mofilers属性,将FINAL约
束去掉,则可修改

ips:
数组反射


声明数组对象
Array.newInstance(int.class, 3);
数组class

Class intArray = Class.forName("[I");
Class byteArray = Class.forName("[B");
Class stringArrayClass = Class.forName("[Ljava.lang.String;");

// 上面的字符串可以通过打印来观察
System.out.println(LinkOption[].class);
// 输出
class [Ljava.nio.file.LinkOption;

// 取巧
Class theClass = getClass(theClassName);
Class stringArrayClass = Array.newInstance(theClass,
0).getClass();

获取数组值

Array.get(obj, index);

2.4. 双亲委派

双亲委派的作用:

  1. 为了保证相同的class文件,在使用的时候,是相同的对象,jvm设计的时候,采
    用了双亲委派的方式来加载类,防止相同类的重复加载。一般来说应用启动的
    时候会有统一的AppClassLoader来加载项目里的class
  2. 保证启动类加载器优先加载,防止JDK的class被篡改

打破双亲委派:ClassLoader#loadClass方法就是以双亲委派逻辑编写的,只要继承
ClassLoader重写loadClass去掉双亲委派的代码就可以打破双亲委派。也可以通过
defineclass绕过loadclass。

tomcat类加载器需要破坏双亲委派机制

tomcat是个web容器,要解决以下问题

  1. 一个web容器可能要部署两个或者多个应用程序,不同的应用程序,可能会依赖
    同一个第三方类库的不同版本,因此要保证每一个应用程序的类库都是独立、相互隔离
  2. 部署在同一个web容器中的相同类库的相同版本可以共享,否则,会有重复的类
    库被加载进JVM
  3. web容器也有自己的类库,不能和应用程序的类库混淆,需要相互隔离
  4. web容器支持jsp文件修改后不用重启,jsp文件也是要编译成.class文件的,支持
    HotSwap功能
  • 架构图

tomcat自己定义的类加载器

  1. CommonClassLoader:tomcat最基本的类加载器,加载路径中的class可以被tomcat和
    各个webapp访问
  2. CatalinaClassLoader:tomcat私有的类加载器,webapp不能访问其加载路径下的
    class,即对webapp不可见
  3. SharedClassLoader:各个webapp共享的类加载器,对tomcat不可见
  4. WebappClassLoader:webapp私有的类加载器,只对当前webapp可见
  5. JspClassLoader
  • 每一个web应用程序对应一个WebappClassLoader,每一个jsp文件对应一个
    JspClassLoader,所以这两个类加载器有多个实例

Tomcat 中有 4 类容器组件,从上至下依次是:

  1. Engine,实现类为 org.apache.catalina.core.StandardEngine
  2. Host,实现类为 org.apache.catalina.core.StandardHost
  3. Context,实现类为 org.apache.catalina.core.StandardContext
  4. Wrapper,实现类为 org.apache.catalina.core.StandardWrapper
     

“从上至下” 的意思是,它们之间是存在父子关系的

  • Engine:最顶层容器组件,其下可以包含多个 Host
  • Host:一个 Host 代表一个虚拟主机,其下可以包含多个 Context
  • Context:一个 Context 代表一个 Web 应用,其下可以包含多个 Wrapper
  • Wrapper:一个 Wrapper 代表一个 Servlet
    srping

3. 动态注册方式注入

关于各个协议和组件的内存马的构造思路其实都大同小异

  1. 分析涉及处理请求的对象,阅读它的源码看看是否能获取请求内容,同时能否
    控制响应内容
  2. 然后分析该对象是如何被注册到内存当中的,最后我们只要模拟下这个过程即
  3. 一般的流程就是request->context->addListener/addFilter

我怎么拿到当前请求的request对象,request对象里一般会有context,context里面一
般都有一些注册组件的方法或者变量存储

需要注意问题

  1. 如果不是web相关类加载器,可能出现类加载报类找不到的问题,这是因为双亲
    委派隔离
  2. 如果用当前上下文的类加载,则相同类名只能加载一次

要解决上面两个问题,就是基于当前上下文的类加载去new一个新的类加载器。
如下用Mlet就是一种方法

new javax.management.loading.MLet(new java.net.URL[0],
conreq.getClass().getClassLoader())

参考代码

java.lang.reflect.Method defineClassMethod =
ClassLoader.class.getDeclaredMethod("defineClass", new Class[]
{byte[].class, int.class, int.class});
defineClassMethod.setAccessible(true);
Class cc = (Class) defineClassMethod.invoke(new
javax.management.loading.MLet(new java.net.URL[0],
conreq.getClass().getClassLoader()), new Object[]{classBytes,
new Integer(0), new Integer(classBytes.length)});

在研究中间件之前,最好去了解下该中间件的发展历史,有哪些版本,因为不同版
本的代码肯定会有不同层度的改动,你的内存马是否适配每个版本是个问题

标签:java,一文,对象,class,内存,字节,Class,加载
From: https://blog.csdn.net/jxiang213/article/details/143231458

相关文章

  • 一文了解软件分析代码审计
    就是要打骨折http://mp.weixin.qq.com/s?__biz=MzkwNjY1Mzc0Nw==&mid=2247486065&idx=2&sn=b30ade8200e842743339d428f414475e&chksm=c0e4732df793fa3bf39a6eab17cc0ed0fca5f0e4c979ce64bd112762def9ee7cf0112a7e76af&scene=21#wechat_redirect《Java代码审计》http:......
  • JavaScript模块化开发
    什么是模块化?◼到底什么是模块化、模块化开发呢?事实上模块化开发最终的目的是将程序划分成一个个小的结构;这个结构中编写属于自己的逻辑代码,有自己的作用域,定义变量名词时不会影响到其他的结构;这个结构可以将自己希望暴露的变量、函数、对象等导出给其结构使用;也可......
  • 如何在Java中实现多态
    JAVA中实现多态的方式有三种:1、继承和重写、2、接口实现、3、通过抽象类。其中接口实现方式特意展开描述:接口为实现多态提供了一个清晰的途径,作为契约规定了一组方法,其实现类按需提供具体功能,Java运行时系统动态确定应调用的具体实现,从而达到多态。一、MULTIPOLYMORPHISMINJAVA......
  • JVM内存池监控
    1.Committed1.1定义:committed指的是JVM从操作系统那里已经获取并承诺给内存池使用的内存量。这部分内存已经被分配给JVM,并且可以立即用于存储对象或数据。1.2特点:committed内存不一定全部被使用,但它保证了JVM在需要时可以直接使用这些内存而不需要再向操作系统申请。当......
  • 【Java源码】基于SpringBoot+小程序的论坛交流系统
    项目介绍本课程演示的是一款基于SpringBoot框架+微信小程序的论坛交流系统,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。包含:项目源码、项目文档、数据库脚本、软件工具等所有资料带你从零开始部署运行本套系统该项目附带的源码资料可作为......
  • C++ 内存管理 堆和栈、内存泄漏、内存分配、指针与内存、智能指针、malloc和free、new
    1.堆和栈的区别1.**管理方式**:-**栈**:自动管理。当函数调用时,局部变量会自动分配在栈上。函数执行完毕后,这些变量会自动释放。-**堆**:手动管理。程序员需要使用`new`来在堆上分配内存,并在不再需要时使用`delete`来释放。2.**使用方式和寿命**:-**栈**:用......
  • java springboot+maven 对接支付宝生成支付二维码;查看订单;取消订单;退款
    支付宝提供了测试环境支付宝沙箱:先登录开放平台https://openhome.alipay.com/develop/manage这边能拿到调用支付宝接口所需要的所有参数代码部分:首先引入依赖:<dependency><groupId>com.alipay.sdk</groupId><artifactId>alipay-sdk-java</art......
  • Java复习16(PTA)
    快递计价器分数20全屏浏览切换布局作者 大数据2021单位 山东科技大学现需要编写一个简易快递计价程序。具体来说:1、抽象快递类Express,其包含一个属性intweight表示快递重量(单位为kg),一个方法getWeight()用于返回快递重量和一个抽象方法getTotal()用于计算快递运费......
  • LeetCode_70. 爬楼梯_java
    1、题目70.爬楼梯https://leetcode.cn/problems/climbing-stairs/假设你正在爬楼梯。需要n阶你才能到达楼顶。每次你可以爬1或2个台阶。你有多少种不同的方法可以爬到楼顶呢?示例1:输入:n=2输出:2解释:有两种方法可以爬到楼顶。1.1阶+1阶2.2阶示例2:输......
  • LeetCode_509. 斐波那契数_java
    1、题目509.斐波那契数https://leetcode.cn/problems/fibonacci-number/斐波那契数(通常用F(n)表示)形成的序列称为斐波那契数列。该数列由0和1开始,后面的每一项数字都是前面两项数字的和。也就是:F(0)=0,F(1)=1F(n)=F(n-1)+F(n-2),其中n>1给定n,请......