01java反序列化基础

news/2025/1/4 6:50:51/文章来源:https://www.cnblogs.com/LSF2456/p/18641569

java反射的相关操作

一些重要的方法

  • 获取类的⽅法: forName

  • 实例化类对象的⽅法: newInstance

  • 获取函数的⽅法: getMethod

  • 执⾏函数的⽅法: invoke

// eg.反射获取任意类的任意方法并执行
import java.lang.reflect.Method;public class ReflectionExample {public static void main(String[] args) {try {// 获取类名Class<?> clazz = Class.forName("com.example.SomeClass");// 获取方法名和参数类型String methodName = "someMethod";Class<?>[] parameterTypes = {String.class, int.class};// 获取方法Method method = clazz.getMethod(methodName, parameterTypes);// 创建类的实例Object obj = clazz.newInstance();// 准备参数Object[] arguments = {"example", 123};// 执行方法Object result = method.invoke(obj, arguments);// 打印结果System.out.println("Method returned: " + result);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (java.lang.reflect.InvocationTargetException e) {e.printStackTrace();}}
}

forName

  • forName 不是获取“类”的唯⼀途径

    • obj.getClass() 如果上下⽂中存在某个类的实例 obj ,那么我们可以直接通过
      obj.getClass() 来获取它的类

    • Test.class 如果你已经加载了某个类,只是想获取到它的 java.lang.Class 对象,那么就直接
      拿它的 class 属性即可。这个⽅法其实不属于反射。

    • Class.forName 如果你知道某个类的名字,想获取到这个类,就可以使⽤ forName 来获取

  • forName的重载

    • forName(String name)和Class forName(String name, boolean initialize, ClassLoader loader)两个重载Class

    • ClassLoader loader就是⼀个“加载器”,一般是一个类的完整路径,如java.lang.Runtime

    • boolean initialize决定是否进行“类初始化”,forName(String name)默认initialize=true

  • 关于类初始化的补充:下面代码的执行顺序为static{}, 构造函数的 super(),{},构造函数,static{}即为类初始化

    public class TrainPrint {{System.out.printf("Empty block initial %s\n", this.getClass());}static {System.out.printf("Static initial %s\n", TrainPrint.class);}public TrainPrint() {super();System.out.printf("Initial %s\n", this.getClass());}
    }
    

newInstance

  • class.newInstance() 的作用就是调用这个类的无参构造函数,于是乎不成功是因为:

    • 你使用的类没有无参构造函数

    • 你使用的类构造函数是私有的,例如java.lang.Runtime,可以采用类的其他静态方法获取实例

newInstance的补充getConstructor

  • Java和C++不同,C++的类必须有一个无参构造函数(显示定义或者编译器自动生成),而Java一但显示定义了任意构造函数,编译器就不会再自动生成无参构造函数,这就造成了一个问题,Java中的类可能没有无参构造函数也没有可获取实例的其他方法,此时就需要getConstructor获取有参构造函数
Class clazz = Class.forName("java.lang.ProcessBuilder");
clazz.getMethod("start").invoke(clazz.getConstructor(List.class).newInstance(
Arrays.asList("calc.exe")));
);

关于类的私有方法

  • 类的私有方法可以通过getDeclared 系列的反射调用,与普通的 getMethod 、 getConstructor 区别是:

    • getMethod 系列方法获取的是当前类中所有公共方法,包括从父类继承的方法

    • getDeclaredMethod 系列方法获取的是当前类中“声明”的方法,是实在写在这个类里的,包括私
      有的方法,但从父类里继承来的就不包含了

    Class clazz = Class.forName("java.lang.Runtime");
    Constructor m = clazz.getDeclaredConstructor();
    // setAccessible(true)修改作用域是必须得
    m.setAccessible(true);
    clazz.getMethod("exec", String.class).invoke(m.newInstance(), "calc.exe");
    

反射的一些特性

  • 无需import类

  • 可以访问私有方法

不同语言的序列化对比

PHP序列化

<?phpclass Connection{protected $link;private $dsn, $username, $password;public function __construct($dsn, $username, $password){$this->dsn = $dsn;$this->username = $username;$this->password = $password;$this->connect();}private function connect(){$this->link = new PDO($this->dsn, $this->username, $this
>password);}}
  • 这里的$link是一个对象,没有自定义__sleep函数时,$link序列化为null.个人的理解,序列化的结果是字符串,对象当然不能直接序列化.
<?phpclass Connection{protected $link;private $dsn, $username, $password;public function __construct($dsn, $username, $password){$this->dsn = $dsn;$this->username = $username;$this->password = $password;$this->connect();}private function connect(){$this->link = new PDO($this->dsn, $this->username, $this
>password);}public function __sleep(){return array('dsn', 'username', 'password');}public function __wakeup(){$this->connect();}
  • 这里添加了一个__sleep,返回由属性组成的数组,又新添了一个__wakeup,这个wakeup完成了反序列化后对于$link的实例化
  • P牛对于PHP反序列化的思考:__wakeup的作用在反序列化后,执行一些初始化操作。但其实我们很少利用序列化数据传递资源类型 的对象,而其他类型的对象,在反序列化的时候就已经赋予其值了。 所以你会发现,PHP的反序列化漏洞,很少是由__wakeup这个方法触发的,通常触发在析构函数 __destruct里。其实大部分PHP反序列化漏洞,都并不是由反序列化导致的,只是通过反序列化可以 控制对象的属性,进而在后续的代码中进行危险操作。

Java序列化

  • 两个条件

    • 实现 java.io.Serializable 接口
    • 所有属性必须是可序列化的。如果有一个属性不是可序列化的,则该属性必须注明是短暂的
  • java对象序列化后和php不同,是字节码而非字符串

  • 下述代码输出了序列化后的person类

package com.individuals.javaSecurity.myclass;import java.io.IOException;
import java.io.Serializable;public class Person implements Serializable {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}private void writeObject(java.io.ObjectOutputStream out) throws IOException {out.defaultWriteObject();out.writeObject("this is a object");}private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException {s.defaultReadObject();String message = (String) s.readObject();System.out.println(message);}
}
import java.io.*;public class SerializationUtils {// 序列化对象并转换为十六进制字符串public static String serializeObjectToHex(Serializable object) throws IOException {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();try (ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {objectOutputStream.writeObject(object);}byte[] serializedBytes = byteArrayOutputStream.toByteArray();return bytesToHex(serializedBytes);}// 反序列化十六进制字符串回对象public static Object deserializeHexToObject(String hexString) throws IOException, ClassNotFoundException {byte[] bytes = hexStringToByteArray(hexString);try (ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(bytes))) {return objectInputStream.readObject();}}// 将字节数组转换为十六进制字符串public static String bytesToHex(byte[] bytes) {StringBuilder hexString = new StringBuilder();for (byte b : bytes) {hexString.append(String.format("%02X", b));}return hexString.toString();}// 将十六进制字符串转换为字节数组public static byte[] hexStringToByteArray(String s) {int len = s.length();byte[] data = new byte[len / 2];for (int i = 0; i < len; i += 2) {data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)+ Character.digit(s.charAt(i + 1), 16));}return data;}
}
import java.io.IOException;import static com.individuals.javaSecurity.utils.SerializationUtils.*;public class TestSer {public static void main(String[] args) throws IOException {Person person = new Person("lda",123);System.out.println(serializeObjectToHex(person));}
}
  • java -jar SerializationDumper-v1.13.jar 序列化对象.值得注意的是我们在序列化时,写入的字符串"this is a object"在objectAnnotation中
STREAM_MAGIC - 0xac ed
STREAM_VERSION - 0x00 05
ContentsTC_OBJECT - 0x73TC_CLASSDESC - 0x72classNameLength - 43 - 0x00 2bValue - com.individuals.javaSecurity.myclass.Person - 0x636f6d2e696e646976696475616c732e6a61766153656375726974792e6d79636c6173732e506572736f6eserialVersionUID - 0xf9 30 f2 ab 12 b1 36 32newHandle 0x00 7e 00 00classDescFlags - 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLEfieldCount - 2 - 0x00 02Fields0:Int - I - 0x49fieldNameLength - 3 - 0x00 03Value - age - 0x6167651:Object - L - 0x4cfieldNameLength - 4 - 0x00 04Value - name - 0x6e616d65className1TC_STRING - 0x74newHandle 0x00 7e 00 01Length - 18 - 0x00 12Value - Ljava/lang/String; - 0x4c6a6176612f6c616e672f537472696e673bclassAnnotationsTC_ENDBLOCKDATA - 0x78superClassDescTC_NULL - 0x70newHandle 0x00 7e 00 02classdatacom.individuals.javaSecurity.myclass.Personvaluesage(int)123 - 0x00 00 00 7bname(object)TC_STRING - 0x74newHandle 0x00 7e 00 03Length - 3 - 0x00 03Value - lda - 0x6c6461objectAnnotationTC_STRING - 0x74newHandle 0x00 7e 00 04Length - 16 - 0x00 10Value - this is a object - 0x746869732069732061206f626a656374TC_ENDBLOCKDATA - 0x78
  • 尝试反序列化我们的序列化对象,可以看当初写入的字符串被打印出来了
import java.io.IOException;import static com.individuals.javaSecurity.utils.SerializationUtils.deserializeHexToObject;public class TestUnser {public static void main(String[] args) throws IOException, ClassNotFoundException {Person person = (Person)deserializeHexToObject("ACED00057372002B636F6D2E696E646976696475616C732E6A61766153656375726974792E6D79636C6173732E506572736F6EF930F2AB12B136320300024900036167654C00046E616D657400124C6A6176612F6C616E672F537472696E673B78700000007B7400036C6461740010746869732069732061206F626A65637478");}
}

image-20240526132132418

  • 这里借助gpt简单解释一下序列化对象中的objectAnnotation和 classAnnotations:
classAnnotations
classAnnotations 是与类相关的注解信息。在序列化过程中,Java会在序列化流中包括与类相关的注解信息,这些信息包括:类的元数据:例如类的名称、类的签名(包括字段和方法的签名)。
类的序列化版本UID:用于验证反序列化时类版本的一致性。
类的父类信息:如果类是从其他类继承而来,这些信息也会被包含在内。
objectAnnotation
objectAnnotation 是与对象相关的注解信息。在序列化过程中,Java会在序列化流中包括与对象相关的注解信息,这些信息包括:对象的字段值:对象的非静态和非瞬态字段的当前值。
引用的其他对象:如果对象包含对其他对象的引用,这些被引用对象也会被序列化。
对象的定制序列化数据:如果类实现了 writeObject 方法,这些方法中自定义序列化的数据也会被包含在 objectAnnotation 中。

Python反序列化

  • Python反序列化和Java、PHP有个显著的区别,就是Python的反序列化过程实际上是在执行一个基于 栈的虚拟机。我们可以向栈上增、删对象,也可以执行一些指令,比如函数的执行等,甚至可以用这个 虚拟机执行一个完整的应用程序。 所以,Python的反序列化可以立即导致任意函数、命令执行漏洞,与需要gadget的PHP和Java相比更加 危险。

总结

  • 总结一下,从危害上来看,Python的反序列化危害是最大的;从应用广度上来看,Java的反序列化是最 常被用到的;从反序列化的原理上来看,PHP和Java是类似又不尽相同的。

补充:SerializationDumper的使用

  • SerializationDumper是一个分析序列化对象的工具
  • 使用方法很简单:java -jar SerializationDumper-v1.13.jar ,后面可以直接加反序列化对象的十六进制串或者从文件读取

几个案例分析

  • Java Object Serialization Specification: 6 - Object Serialization Stream Protocol (oracle.com) java序列化协议文档
  • 下图为java序列化对象的结构分析图
    alt text
public class Person implements Serializable {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;}private void writeObject(java.io.ObjectOutputStream out) throws IOException {out.defaultWriteObject();out.writeObject("this is a object");}private void readObject(java.io.ObjectInputStream s)throws IOException, ClassNotFoundException {s.defaultReadObject();String message = (String) s.readObject();System.out.println(message);}
}
public class TestSer {public static void main(String[] args) throws IOException {Person person = new Person("lda",123);System.out.println(serializeObjectToHex(person));}
}
STREAM_MAGIC - 0xac ed
STREAM_VERSION - 0x00 05
ContentsTC_OBJECT - 0x73TC_CLASSDESC - 0x72classNameLength - 43 - 0x00 2bValue - com.individuals.javaSecurity.myclass.Person - 0x636f6d2e696e646976696475616c732e6a61766153656375726974792e6d79636c6173732e506572736f6eserialVersionUID - 0xf9 30 f2 ab 12 b1 36 32newHandle 0x00 7e 00 00classDescFlags - 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLEfieldCount - 2 - 0x00 02Fields0:Int - I - 0x49fieldNameLength - 3 - 0x00 03Value - age - 0x6167651:Object - L - 0x4cfieldNameLength - 4 - 0x00 04Value - name - 0x6e616d65className1TC_STRING - 0x74newHandle 0x00 7e 00 01Length - 18 - 0x00 12Value - Ljava/lang/String; - 0x4c6a6176612f6c616e672f537472696e673bclassAnnotationsTC_ENDBLOCKDATA - 0x78superClassDescTC_NULL - 0x70newHandle 0x00 7e 00 02classdatacom.individuals.javaSecurity.myclass.Personvaluesage(int)123 - 0x00 00 00 7bname(object)TC_STRING - 0x74newHandle 0x00 7e 00 03Length - 3 - 0x00 03Value - lda - 0x6c6461objectAnnotationTC_STRING - 0x74newHandle 0x00 7e 00 04Length - 16 - 0x00 10Value - this is a object - 0x746869732069732061206f626a656374TC_ENDBLOCKDATA - 0x78

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

单元化架构最佳实践指南!

0 前言单元化架构通过减少故障影响范围来增强系统的弹性。 单元化架构是对于那些无法接受停机或可能对最终用户产生负面影响的系统的良好选择。 单元化架构可能很复杂,有一些最佳实践可以遵循,以提高成功的机会。 在推出单元化架构或将现有的云原生/微服务架构改造/转变为单元…

苹果鼠标有望加入 AI 语音控制功能;Accent Oracl :准确识别你口音的 AI 丨 RTE 开发者日报

开发者朋友们大家好:这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文章 」、「有看点的 会议 」,但内容仅代表编辑…

计算机网络复习应用层(第六章)

应用层 应用层的共同特点:C/S或者P2P、推/拉、面向文本/面向二进制域名系统(DNS) 名字到IP地址的解析互联网的域名空间 互联网采用了层次树状结构的命名方式采用:.四级域名.三级域名.二级域名.顶级域名 其中本地域名服务器向根域名服务器发出DNS请求,根域名服务器进行查找…

【有源码】流浪宠物管理系统+SpringBoot+VUE+前后端分离

学弟,学妹好,我是爱学习的学姐,今天带来一款优秀的项目:流浪宠物管理系统。 本文介绍了系统功能与部署安装步骤,如果您有任何问题,也请联系学姐,偶现在是经验丰富的程序员! 一. 系统演示 管理后台-截图前端-截图视频演示 https://githubs.xyz/show/326.mp4二. 系统概述…

.Net Core 管道底层源码实现

在 .NET Core 中,请求处理管道是一个中间件(Middleware)链,用于处理 HTTP 请求并生成响应。管道的底层实现基于 Microsoft.AspNetCore.Http 命名空间中的一些核心类和接口1. 核心组件 1.1 HttpContext• HttpContext 是 HTTP 请求和响应的核心抽象,封装了请求信息(如请求…

JavaScript代码安全性提升:选择和使用JS混淆工具的指南

https://toolin.cn/jsfuckhttps://maimai.cn/article/detail?fid=1827257627&efid=382Pa05uQ_i7jAl6rmq5tg在Web开发中,JavaScript是一种常用的脚本语言,然而,由于其代码容易被他人轻易获取和修改,为了保护JavaScript代码的安全性和版权,我们需要使用JS混淆工具。本文…

AI 驱动研发模式升级,蓝凌软件探索效率提升之道

蓝凌软件在引入通义灵码后取得了较明显的效果。目前,蓝凌软件已使用灵码的开发人员中,周活跃用户占比超过90%、根据代码库自动生成的代码占比超33%、代码智能补全占比29%,代码注释率提升了15%,有效提升了产品代码工程化的效能。深圳市蓝凌软件股份有限公司(以下简称蓝凌软…

“越用越上瘾”,中华财险 60% 研发人员用通义灵码提效

保险业被看成是社会“稳定器”和经济“助推器”,近年来已驶入数字化发展快车道。在 AI、大模型当道的今天,保险行业的研发流程、产品设计、场景拓展等业务链条各环节,都值得用大模型进行重塑。保险业被看成是社会“稳定器”和经济“助推器”,近年来已驶入数字化发展快车道。…

Oracle数据库表空间

SELECT tablespace_name, SUM(BYTES)/1024/1024 "Free Space (MB)" FROM dba_free_space GROUP BY tablespace_name; 追加表空间的语句,里面的路径是服务器的实际路径: ALTER TABLESPACE UNDOTBS1 ADD DATAFILE Z:/app/oradata/MELALEUCADB/UNDOTBS03.DBF SIZE 10…

通义灵码入选南方日报 2024 数字化年度标杆

在数字化浪潮席卷全球的今天,技术创新和智能化转型成为企业发展的关键。在这样的背景下,通义灵码凭借其卓越的性能和创新能力,荣获南方日报2024数字化年度标杆大奖,成为数字化转型领域的佼佼者。在数字化浪潮席卷全球的今天,技术创新和智能化转型成为企业发展的关键。在这…

拿了一个奖:年度高价值技术团队

在数字经济的大背景下,“新质生产力”的要义就在科技创新,目的是实现高质量发展。值得关注的是,“新质生产力”最终落脚点还在生产力,未来主战场仍然是实体产业。与过去不同的是,未来的实体产业发展不再是“单点开花”,而是全产业链的体系化升级。在这个过程中,人工智能…