MySql逻辑语句的执行顺序

执行顺序 (7) SELECT (8) DISTINCT <select_list> (1) FROM <left_table> // 这里FROM不止处理后面这一张,包括JOIN 中的,一起处理,笛卡尔积。 (3) <join_type> JOIN <right_table> // 这里指的是根据JOIN的方式,确定保留的内容。 (2) ON <join_condition> // 先ON 再 JOIN逻辑。 (4) WHERE <where_condition> // 先WHERE条件过滤、再GROUP BY分组、再HAVING过滤、再DISTINCT排重、再ORDER BY排序、最后LIMIT。 (5) GROUP BY <group_by_list> (6) HAVING <having_condition> (9) ORDER BY <order_by_condition> (10) LIMIT <limit_number> 连接器:认证、授权 查询缓存(8.0移除) 分析器:词法分析、语法分析 优化器:连接优化、索引选取 执行器:访问权限、引擎API调用

ASM 库的介绍和使用

转自这篇文章主要介绍 ASM 库的结构、主要的 API,并且通过两个示例说明如何通过 ASM 修改 .class 文件中的方法和属性。 另ASM相关,在JDK中也有,只是相关代码只开放给内部 以及Spring、aspectJ都有ASM相关的。 ASM 是一个 Java 字节码操控框架。它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。Java class 被存储在严格格式定义的 .class 文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。ASM 从类文件中读入信息后,能够改变类行为,分析类信息,甚至能够根据用户要求生成新类。 一. ASM 的结构 ASM 库是一款基于 Java 字节码层面的代码分析和修改工具。ASM 可以直接生产二进制的 class 文件,也可以在类被加载入 JVM 之前动态修改类行为。ASM 库的结构如下所示: Core:为其他包提供基础的读、写、转化Java字节码和定义的API,并且可以生成Java字节码和实现大部分字节码的转换,在 访问者模式和 ASM 中介绍的几个重要的类就在 Core API 中:ClassReader、ClassVisitor 和 ClassWriter 类. Tree:提供了 Java 字节码在内存中的表现 Commons:提供了一些常用的简化字节码生成、转换的类和适配器 Util:包含一些帮助类和简单的字节码修改类,有利于在开发或者测试中使用 XML:提供一个适配器将XML和SAX-comliant转化成字节码结构,可以允许使用XSLT去定义字节码转化 […]

访问者模式和 ASM

转自 之前的文章介绍了 .class 文件的结构、JVM 对 .class 文件加载以及在 JVM 中是怎么执行程序的,接下来的文章会介绍 ASM 的使用,ASM 是运用访问者模式设计的,本篇文章就介绍一下访问者模式的概念以及其在 ASM 中的应用。 一. 概述 & 定义 定义:封装某些作用于某种数据结构中各元素的操作,它可以在不改变数据结构的前提下定义作用于这些数据元素的新的操作 意图:主要将数据结构和数据操作分离 主要解决:稳定的数据结构和易变的操作的解耦 适用场景: 假如一个对象中存在着一些与本对象不相干(或者关系较弱)的操作,可以使用访问者模式把这些操作封装到访问者中去,这样便避免了这些不相干的操作污染这个对象。 假如一组对象中,存在着相似的操作,可以将这些相似的操作封装到访问者中去,这样便避免了出现大量重复的代码 访问者模式适用于对功能已经确定的项目进行重构的时候适用,因为功能已经确定,元素类的数据结构也基本不会变了;如果是一个新的正在开发中的项目,在访问者模式中,每一个元素类都有它对应的处理方法,每增加一个元素类都需要修改访问者类,修改起来相当麻烦。 二. 示例 如果老师教学反馈得分大于等于85分、学生成绩大于等于90分,则可以入选成绩优秀奖;如果老师论文数目大于8、学生论文数目大于2,则可以入选科研优秀奖。 在这个例子中,老师和学生就是Element,他们的数据结构稳定不变。从上面的描述中,我们发现,对数据结构的操作是多变的,一会儿评选成绩,一会儿评选科研,这样就适合使用访问者模式来分离数据结构和操作。 2.1 创建抽象元素 2.2 创建具体元素 创建两个具体元素 Student 和 Teacher,分别实现 Element 接口 2.3 创建抽象访问者 2.4 创建具体访问者 创建一个根据分数评比的具体访问者 GradeSelection,实现 Visitor 接口 2.5 访问者代码调用 上述代码即是一个简单的访问者模式的示例代码,输出如下所示: 上述代码可以分为三步:1. 创建一个元素类的对象2. 创建一个访问类的对象3. 元素对象通过 Element#accept(Visitor […]

JUC整理笔记-VarHandle

前面整理完 Unsafe ,不得不去了解下 java.lang.invoke.Varhandle 。如前面文章所说, Unsafe 是不建议开发者直接使用的,因为 Unsafe 所操作的并不属于Java标准,会容易带来一些安全性的问题。JDK9 之后,官方推荐使用 java.lang.invoke.Varhandle 来替代 Unsafe 大部分功能,对比 Unsafe ,Varhandle 有着相似的功能,但会更加安全,并且,在并发方面也提高了不少性能。以下有用到MethodHandle,可以先去了解一下。 简介 Varhandle是对变量或参数定义的变量系列的动态强类型引用,包括静态字段,非静态字段,数组元素或堆外数据结构的组件。 在各种访问模式下都支持访问这些变量,包括简单的读/写访问,volatile 的读/写访问以及 CAS (compare-and-set)访问。简单来说 Variable 就是对这些变量进行绑定,通过 Varhandle 直接对这些变量进行操作。 实例 目标实体类 public class Demo {   public int publicVar = 1;   protected int protectedVar = 2;   private int privateVar = 3;   public int[] arrayData […]

JUC整理笔记-Unsafe

转:JFound JUC(java.util.concurrent)的开始,可以说是从Unsafe类开始。 Unsafe 简介 Unsafe在sun.misc 下,顾名思义,这是一个不安全的类,因为Unsafe类所操作的并不属于Java标准,Java的一系列内存操作都是交给jvm的,而Unsafe类却能有像C语言的指针一样直接操作内存的能力,同时也会带来了指针的问题。过度使用Unsafe类的话,会使出错率变得更大,因此官方才命名为Unsafe,并且不建议使用,连注释的没有。 而为了安全使用Unsafe,Unsafe类只允许jdk自带的类使用,从下面的代码中可以看出 public static Unsafe getUnsafe() {     Class<?> caller = Reflection.getCallerClass();     if (!VM.isSystemDomainLoader(caller.getClassLoader()))         throw new SecurityException(“Unsafe”);     return theUnsafe; } 如果当前Class是非系统加载的(也就是caller.getClassLoader()不为空),直接抛出SecurityException 。 在java9之后,又出现了一个jdk.internal.misc.Unsafe类,其功能与sun.misc.Unsafe类是一样的,唯一不一样的是在 getSafe() 的时候,jdk.internal.misc.Unsafe是没有做校验的,但是jdk包下的代码,应用开发时是不能直接调用的,而且在java9之后,两个Unsafe类都有充足的注释。 获取Unsafe Unsafe类里有这样的一个field。 private static final Unsafe theUnsafe = new Unsafe(); 也就是说虽然不能直接拿到Unsafe对象,但是还是可以通过反射去获取的。 private static Unsafe getUnsafe() throws […]

Java 字符串对象

new String都是在堆上创建字符串对象。当调用 intern() 方法时,编译器会将字符串添加到常量池中(stringTable维护),并返回指向该常量的引用。 通过字面量赋值创建字符串(如:String str=”twm”)时,会先在常量池中查找是否存在相同的字符串,若存在,则将栈中的引用直接指向该字符串;若不存在,则在常量池中生成一个字符串,再将栈中的引用指向该字符串。 常量字符串的“+”操作,编译阶段直接会合成为一个字符串。如string str=”JA”+”VA”,在编译阶段会直接合并成语句String str=”JAVA”,于是会去常量池中查找是否存在”JAVA”,从而进行创建或引用。 对于final字段,编译期直接进行了常量替换。而对于非final字段则是在运行期进行赋值处理的。 常量字符串和变量拼接时(如:String str3=baseStr + “01”;)会调用stringBuilder.append()在堆上创建新的对象 JDK 1.7后,intern方法还是会先去查询常量池中是否有已经存在,如果存在,则返回常量池中的引用,这一点与之前没有区别,区别在于,如果在常量池找不到对应的字符串,则不会再将字符串拷贝到常量池,而只是在常量池中生成一个对原字符串的引用。简单的说,就是往常量池放的东西变了:原来在常量池中找不到时,复制一个副本放到常量池,1.7后则是将在堆上的地址引用复制到常量池。 JVM 常量池中存储的是对象还是引用 如果是runtime constant pool(而不是interned string pool / StringTable之类的其他东西)的话,其中的引用类型常量(例如CONSTANT_String、CONSTANT_Class、CONSTANT_MethodHandle、CONSTANT_MethodType之类)都存的是引用,实际的对象还是存在Java heap上的。

Java 元空间

这里包含一个问题,为何要移除方法区 改用元空间:https://openjdk.java.net/projects/jdk8/features 移除永久代的工作从JDK1.7就开始了。JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap。 为什么移除持久代。在JDK1.8移除了永久代,添加了元空间 它的大小是在启动时固定好的——很难验证并进行调优。-XX:MaxPermSize HotSpot的内部类型也是Java对象:它可能会在Full GC中被移动,非强类型,难以跟踪调试,需要存储元数据的元数据信息。 简化垃圾回收:对每一个回收集使用专门的元数据迭代器。 可以在GC不进行暂停的情况下并发地释放类数据。 使得原来受限于持久代的一些改进未来有可能实现 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小: -XX:MetaspaceSize,初始空间大小,达到该值就会触发垃圾收集进行类型卸载,同时GC会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过MaxMetaspaceSize时,适当提高该值。 -XX:MaxMetaspaceSize,最大空间,默认是没有限制的。 除了上面两个指定大小的选项以外,还有两个与 GC 相关的属性: -XX:MinMetaspaceFreeRatio,在GC之后,最小的Metaspace剩余空间容量的百分比,减少为分配空间所导致的垃圾收集 -XX:MaxMetaspaceFreeRatio,在GC之后,最大的Metaspace剩余空间容量的百分比,减少为释放空间所导致的垃圾收集 元空间的特性 充分利用了Java语言规范中的好处:类及相关的元数据的生命周期与类加载器的一致 每个加载器有专门的存储空间 只进行线性分配 不会单独回收某个类 省掉了GC扫描及压缩的时间 元空间里的对象的位置是固定的 如果GC发现某个类加载器不再存活了,会把相关的空间整个回收掉 元空间的内存分配模型 绝大多数的类元数据的空间都从本地内存中分配 用来描述类元数据的类也被删除了 分元数据分配了多个虚拟内存空间 给每个类加载器分配一个内存块的列表。块的大小取决于类加载器的类型; sun/反射/代理对应的类加载器的块会小一些 归还内存块,释放内存块列表 一旦元空间的数据被清空了,虚拟内存的空间会被回收掉 减少碎片的策略 元空间内存管理 元空间的内存管理由元空间虚拟机来完成。先前,对于类的元数据我们需要不同的垃圾回收器进行处理,现在只需要执行元空间虚拟机的C++代码即可完成。在元空间中,类和其元数据的生命周期和其对应的类加载器是相同的。话句话说,只要类加载器存活,其加载的类的元数据也是存活的,因而不会被回收掉。 准确的来说,每一个类加载器的存储区域都称作一个元空间,所有的元空间合在一起就是我们一直说的元空间。当一个类加载器被垃圾回收器标记为不再存活,其对应的元空间会被回收。在元空间的回收过程中没有重定位和压缩等操作。但是元空间内的元数据会进行扫描来确定Java引用。 元空间虚拟机负责元空间的分配,其采用的形式为组块分配。组块的大小因类加载器的类型而异。在元空间虚拟机中存在一个全局的空闲组块列表。当一个类加载器需要组块时,它就会从这个全局的组块列表中获取并维持一个自己的组块列表。当一个类加载器不再存活,那么其持有的组块将会被释放,并返回给全局组块列表。类加载器持有的组块又会被分成多个块,每一个块存储一个单元的元信息。组块中的块是线性分配(指针碰撞分配形式)。组块分配自内存映射区域。这些全局的虚拟内存映射区域以链表形式连接,一旦某个虚拟内存映射区域清空,这部分内存就会返回给操作系统。 元空间的问题 元空间虚拟机采用了组块分配的形式,同时区块的大小由类加载器类型决定。类信息并不是固定大小,因此有可能分配的空闲区块和类需要的区块大小不同,这种情况下可能导致碎片存在。元空间虚拟机目前并不支持压缩操作,所以碎片化是目前最大的问题。

@Controller、@Service、@Repository等放在接口还是实现类上

这里以@Service为例,其他也一样 @Service注解是标注在实现类上的 因为@Service是把spring容器中的bean进行实例化,也就是等同于new操作,只有实现类是可以进行new实例化的,而接口则不能,所以是加在实现类上的。 IOC的思想:A不直接调用B,而是通过调用生产B的工厂(工厂模式),由B工厂来创建B 有三种普通注入方式,分为构造函数的注入、属性注入、接口注入。 将注入方式交给第三方,通过bean的注解,想调用时直接通过注解注入。 所以通过注解注入bean,就是实例化依赖类的方式, 这也是为什么要将@Service和@Repository放到实现类上面而不是接口类上面, 接口只是一个规范,需要各种实现类去实现这个接口,我们要用的就是这些实用类的方法。 如果采用@Autowired来注解,则同样无需指定name属性,若是实现该接口有多个类,则需要通过@Qualifier来做区分(这里之前疑惑了一下,因为在上一层都是针对接口做的操作。另外有一点就是,因为@Service是标注在实现类上的,所以动态代理为cglib,这个debug一下就看到了) 但Mybatis和MapStruct都有个叫@Mapper的注解,这个是放在接口上的,这个属于其他情况,可忽略。

高版本JDK的JNDI注入

转自 「声明:本博客中涉及到的相关漏洞均为官方已经公开并修复的漏洞,涉及到的安全技术也仅用于企业安全建设和安全对抗研究。本文仅限业内技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。」 写在前面 Java JNDI注入有很多种不同的利用载荷,而这些Payload分别会面临一些限制。笔者在实际测试过程中也遇到过很多有限制的情况,这里做个梳理并分享下如何绕过这些限制。关于JNDI注入和RMI的基础知识,可以在我之前的文章《深入理解JNDI注入与Java反序列化漏洞利用》中获取。我们先看看JDK对各种Payload有什么限制: 1. RMI Remote Object Payload(限制较多,不常使用) 攻击者实现一个RMI恶意远程对象并绑定到RMI Registry上,编译后的RMI远程对象类可以放在HTTP/FTP/SMB等服务器上,这个Codebase地址由远程服务器的 java.rmi.server.codebase 属性设置,供受害者的RMI客户端远程加载,RMI客户端在 lookup() 的过程中,会先尝试在本地CLASSPATH中去获取对应的Stub类的定义,并从本地加载,然而如果在本地无法找到,RMI客户端则会向远程Codebase去获取攻击者指定的恶意对象,这种方式将会受到 useCodebaseOnly 的限制。利用条件如下: RMI客户端的上下文环境允许访问远程Codebase。 属性 java.rmi.server.useCodebaseOnly 的值必需为false。 然而从JDK 6u45、7u21开始,java.rmi.server.useCodebaseOnly 的默认值就是true。当该值为true时,将禁用自动加载远程类文件,仅从CLASSPATH和当前VM的java.rmi.server.codebase 指定路径加载类文件。使用这个属性来防止客户端VM从其他Codebase地址上动态加载类,增加了RMI ClassLoader的安全性。 Changelog: JDK 6u45 https://docs.oracle.com/javase/7/docs/technotes/guides/rmi/relnotes.html JDK 7u21 http://www.oracle.com/technetwork/java/javase/7u21-relnotes-1932873.html 2. RMI + JNDI Reference Payload 这也是我们在《深入理解JNDI注入与Java反序列化漏洞利用》中主要讨论的利用方式。攻击者通过RMI服务返回一个JNDI Naming Reference,受害者解码Reference时会去我们指定的Codebase远程地址加载Factory类,但是原理上并非使用RMI Class Loading机制的,因此不受 java.rmi.server.useCodebaseOnly 系统属性的限制,相对来说更加通用。 但是在JDK 6u132, JDK 7u122, JDK 8u113 JDK 6u141, […]

JNDI注入与Java反序列化漏洞

转自 「声明:本博客中涉及到的相关漏洞均为官方已经公开并修复的漏洞,涉及到的安全技术也仅用于企业安全建设和安全对抗研究。本文仅限业内技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。」 0. 前言 在Java反序列化漏洞挖掘或利用的时候经常会遇到RMI、JNDI、JRMP这些概念,其中RMI是一个基于序列化的Java远程方法调用机制。作为一个常见的反序列化入口,它和反序列化漏洞有着千丝万缕的联系。除了直接攻击RMI服务接口外(比如:CVE-2017-3241),我们在构造反序列化漏洞利用时也可以结合RMI方便的实现远程代码执行。 在2016年的BlackHat上,@pwntester分享了通过JNDI注入进行RCE利用的方法。这一利用方式在2016年的spring-tx.jar反序列化漏洞和2017年FastJson反序列化漏洞利用等多个场景中均有出现。 本文争取简单易懂的介绍一下RMI机制和JNDI注入利用方式,并且以JdbcRowSetImpl利用链和FastJson反序列化漏洞为例,记录真实的远程利用过程中可能遇到的问题和解决,希望能给研究这块的新同学一些参考,如有错误欢迎交流指正。 1. 关于RMI 这一节主要介绍一下RMI的调用流程、RMI注册表以及动态加载类的概念。 1.1 远程方法调用 远程方法调用是分布式编程中的一个基本思想。实现远程方法调用的技术有很多,比如:CORBA、WebService,这两种都是独立于编程语言的。而RMI(Remote Method Invocation)是专为Java环境设计的远程方法调用机制,远程服务器实现具体的Java方法并提供接口,客户端本地仅需根据接口类的定义,提供相应的参数即可调用远程方法。RMI依赖的通信协议为JRMP(Java Remote Message Protocol ,Java 远程消息交换协议),该协议为Java定制,要求服务端与客户端都为Java编写。这个协议就像HTTP协议一样,规定了客户端和服务端通信要满足的规范。在RMI中对象是通过序列化方式进行编码传输的。 1.2 远程对象 使用远程方法调用,必然会涉及参数的传递和执行结果的返回。参数或者返回值可以是基本数据类型,当然也有可能是对象的引用。所以这些需要被传输的对象必须可以被序列化,这要求相应的类必须实现 java.io.Serializable 接口,并且客户端的serialVersionUID字段要与服务器端保持一致。 任何可以被远程调用方法的对象必须实现 java.rmi.Remote 接口,远程对象的实现类必须继承UnicastRemoteObject类。如果不继承UnicastRemoteObject类,则需要手工初始化远程对象,在远程对象的构造方法的调用UnicastRemoteObject.exportObject()静态方法。如下: 注: IHello是客户端和服务端共用的接口(客户端本地必须有远程对象的接口,不然无法指定要调用的方法,而且其全限定名必须与服务器上的对象完全相同),HelloImpl是一个服务端远程对象,提供了一个sayHello方法供远程调用。它没有继承UnicastRemoteObject类或者实现java.rmi.Remote接口,而是在构造方法中调用了UnicastRemoteObject.exportObject()。 在JVM之间通信时,RMI对远程对象和非远程对象的处理方式是不一样的,它并没有直接把远程对象复制一份传递给客户端,而是传递了一个远程对象的Stub,Stub基本上相当于是远程对象的引用或者代理。Stub对开发者是透明的,客户端可以像调用本地方法一样直接通过它来调用远程方法。Stub中包含了远程对象的定位信息,如Socket端口、服务端主机地址等等,并实现了远程调用过程中具体的底层网络通信细节,所以RMI远程调用逻辑是这样的: 从逻辑上来看,数据是在Client和Server之间横向流动的,但是实际上是从Client到Stub,然后从Skeleton到Server这样纵向流动的。 Server端监听一个端口,这个端口是JVM随机选择的; Client端并不知道Server远程对象的通信地址和端口,但是Stub中包含了这些信息,并封装了底层网络操作; Client端可以调用Stub上的方法; Stub连接到Server端监听的通信端口并提交参数; 远程Server端上执行具体的方法,并返回结果给Stub; Stub返回执行结果给Client端,从Client看来就好像是Stub在本地执行了这个方法一样; 那怎么获取Stub呢? 1.3 RMI注册表 Stub的获取方式有很多,常见的方法是调用某个远程服务上的方法,向远程服务获取存根。但是调用远程方法又必须先有远程对象的Stub,所以这里有个死循环问题。JDK提供了一个RMI注册表(RMIRegistry)来解决这个问题。RMIRegistry也是一个远程对象,默认监听在传说中的1099端口上,可以使用代码启动RMIRegistry,也可以使用rmiregistry命令。 要注册远程对象,需要RMI URL和一个远程对象的引用。 LocateRegistry.getRegistry()会使用给定的主机和端口等信息本地创建一个Stub对象作为Registry远程对象的代理,从而启动整个远程调用逻辑。服务端应用程序可以向RMI注册表中注册远程对象,然后客户端向RMI注册表查询某个远程对象名称,来获取该远程对象的Stub。 使用RMI Registry之后,RMI的调用关系是这样的: 所以其实从客户端角度看,服务端应用是有两个端口的,一个是RMI Registry端口(默认为1099),另一个是远程对象的通信端口(随机分配的)。这个通信细节比较重要,真实利用过程中可能会在这里遇到一些坑。 1.4 动态加载类 RMI核心特点之一就是动态类加载,如果当前JVM中没有某个类的定义,它可以从远程URL去下载这个类的class,动态加载的对象class文件可以使用Web服务的方式进行托管。这可以动态的扩展远程应用的功能,RMI注册表上可以动态的加载绑定多个RMI应用。对于客户端而言,服务端返回值也可能是一些子类的对象实例,而客户端并没有这些子类的class文件,如果需要客户端正确调用这些子类中被重写的方法,则同样需要有运行时动态加载额外类的能力。客户端使用了与RMI注册表相同的机制。RMI服务端将URL传递给客户端,客户端通过HTTP请求下载这些类。 这个概念比较重要,JNDI注入的利用方法中也借助了动态加载类的思路。 这里涉及到的角色:客户端、RMI注册表、远程对象服务器、托管class文件的Web服务器可以分别位于不同的主机上: 2. […]

lWoHvYe 无悔,专一