首页 > 编程语言 >01java反序列化基础

01java反序列化基础

时间:2025-01-02 19:18:24浏览次数:5  
标签:java String 基础 class 0x00 01java 序列化 public

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序列化

<?php
 class 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.个人的理解,序列化的结果是字符串,对象当然不能直接序列化.
<?php
 class 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
Contents
  TC_OBJECT - 0x73
    TC_CLASSDESC - 0x72
      className
        Length - 43 - 0x00 2b
        Value - com.individuals.javaSecurity.myclass.Person - 0x636f6d2e696e646976696475616c732e6a61766153656375726974792e6d79636c6173732e506572736f6e
      serialVersionUID - 0xf9 30 f2 ab 12 b1 36 32
      newHandle 0x00 7e 00 00
      classDescFlags - 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLE
      fieldCount - 2 - 0x00 02
      Fields
        0:
          Int - I - 0x49
          fieldName
            Length - 3 - 0x00 03
            Value - age - 0x616765
        1:
          Object - L - 0x4c
          fieldName
            Length - 4 - 0x00 04
            Value - name - 0x6e616d65
          className1
            TC_STRING - 0x74
              newHandle 0x00 7e 00 01
              Length - 18 - 0x00 12
              Value - Ljava/lang/String; - 0x4c6a6176612f6c616e672f537472696e673b
      classAnnotations
        TC_ENDBLOCKDATA - 0x78
      superClassDesc
        TC_NULL - 0x70
    newHandle 0x00 7e 00 02
    classdata
      com.individuals.javaSecurity.myclass.Person
        values
          age
            (int)123 - 0x00 00 00 7b
          name
            (object)
              TC_STRING - 0x74
                newHandle 0x00 7e 00 03
                Length - 3 - 0x00 03
                Value - lda - 0x6c6461
        objectAnnotation
          TC_STRING - 0x74
            newHandle 0x00 7e 00 04
            Length - 16 - 0x00 10
            Value - this is a object - 0x746869732069732061206f626a656374
          TC_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 ,后面可以直接加反序列化对象的十六进制串或者从文件读取

几个案例分析

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
Contents
  TC_OBJECT - 0x73
    TC_CLASSDESC - 0x72
      className
        Length - 43 - 0x00 2b
        Value - com.individuals.javaSecurity.myclass.Person - 0x636f6d2e696e646976696475616c732e6a61766153656375726974792e6d79636c6173732e506572736f6e
      serialVersionUID - 0xf9 30 f2 ab 12 b1 36 32
      newHandle 0x00 7e 00 00
      classDescFlags - 0x03 - SC_WRITE_METHOD | SC_SERIALIZABLE
      fieldCount - 2 - 0x00 02
      Fields
        0:
          Int - I - 0x49
          fieldName
            Length - 3 - 0x00 03
            Value - age - 0x616765
        1:
          Object - L - 0x4c
          fieldName
            Length - 4 - 0x00 04
            Value - name - 0x6e616d65
          className1
            TC_STRING - 0x74
              newHandle 0x00 7e 00 01
              Length - 18 - 0x00 12
              Value - Ljava/lang/String; - 0x4c6a6176612f6c616e672f537472696e673b
      classAnnotations
        TC_ENDBLOCKDATA - 0x78
      superClassDesc
        TC_NULL - 0x70
    newHandle 0x00 7e 00 02
    classdata
      com.individuals.javaSecurity.myclass.Person
        values
          age
            (int)123 - 0x00 00 00 7b
          name
            (object)
              TC_STRING - 0x74
                newHandle 0x00 7e 00 03
                Length - 3 - 0x00 03
                Value - lda - 0x6c6461
        objectAnnotation
          TC_STRING - 0x74
            newHandle 0x00 7e 00 04
            Length - 16 - 0x00 10
            Value - this is a object - 0x746869732069732061206f626a656374
          TC_ENDBLOCKDATA - 0x78

标签:java,String,基础,class,0x00,01java,序列化,public
From: https://www.cnblogs.com/LSF2456/p/18641569

相关文章

  • Elasticsearch:基础概念
    一、什么是ElasticsearchElasticsearch是基于ApacheLucene构建的分布式搜索和分析引擎、可扩展数据存储和矢量数据库。它针对生产规模工作负载的速度和相关性进行了优化。使用Elasticsearch可以近乎实时地搜索、索引、存储和分析各种形状和大小的数据。Elasticsearch......
  • 数论基础B
    数论基础B试除法判定质数暴力做法:枚举\(2\)~\(n-1\)的所有数,判断能否将\(n\)整除,如果存在一个数能把\(n\)整数,说明\(n\)不是质数实际上只需要枚举到\(\sqrt{n}\)即可,如果\(a\)是\(n\)的约数,那么\(\frac{n}{a}\)也是\(n\)的约数,我们只需要检验\(min(a,\fr......
  • 2025年Java基础面试题,附答案解析。
    1.Java支持多继承么?不支持,Java不支持多继承。每个类都只能继承一个类,但是可以实现多个接口。2.接口和抽象类的区别是什么?Java提供和支持创建抽象类和接口。它们的实现有共同点,不同点在于:接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。类可......
  • python基础while循环(break、continue)、格式化输出、运算符
    day2while循环break、continue相关知识、格式化输出打印1~100的数字a=1whilea<=100:print(a)a=a+1#continue结束本次循环,开始下一次开启下一次循环break直接结束循环flag=Truewhileflag:print(1)print(2)flag=Falsecontinueprint......
  • 【AI产品经理入门到精通】超详细基础教程:收藏这一篇就够了!祝大家2025年都能成功上岸
    什么是AI产品经理?AI产品经理,顾名思义,就是负责人工智能产品的规划、设计、开发和迭代的专业人士。他们不仅要对市场有敏锐的洞察力,还要对技术有深入的理解,能够将复杂的AI技术转化为用户友好的产品。为什么要学AI产品经理?根据脉脉《2023年人才报告》显示:人工智能成为2023......
  • Openlayers零基础教程【6】geojson实现点要素
    1.geojson定义geojson数据是矢量数据,是包含地理信息的json数据,格式是以key:value的形式存在的。后缀以geojson结尾2.geojson设置一个点要素本篇内容我们主要介绍使用geojson设置一个点要素,效果如下图所示。3.实现步骤:3.1.创建geojson数据/*创建geojson数据......
  • 大语言模型【基础】(二)微调需要多少算力?
    微调模型需要多少的GPU显存?一、模型【训练】占用显存【QWen2.5-32B为例】模型配置情况如下所示方法一:较为精确估计全量微调占用情况结论根据模型配置和假设的batchsize、序列长度:总显存需求:约388GB所需卡数:至少13张昇腾910B卡才能满足显存需求,推荐使用1......
  • 01xArduino程序基础
    Arduino程序基础使用C++编程,基本参考C++语法。每一句结尾用分号,注释用//,全大写单词是特有字符,不要乱用。函数用{}套起来。voidsetup(){//putyoursetupcodehere,torunonce://这里的代码在开始的时候运行一次codedoingsomething;//每一行代码......
  • c# 元组序列化
        在C#中,可以使用System.Text.Json或Newtonsoft.Json库来对元组进行序列化。以下是使用这两个库进行元组序列化的示例代码。使用System.Text.Json:  usingSystem;usingSystem.Text.Json; vartuple=(Name:"John",Age:30);stringjson......
  • 《docker基础篇:5.本地镜像发布到阿里云》
    @目录5.本地镜像发布到阿里云本人其他相关文章链接5.本地镜像发布到阿里云案例使用步骤:1)本地镜像素材原型2)阿里云开发者平台3)创建仓库镜像4)将镜像推送到阿里云5)将阿里云上的镜像下载到本地6)运行注意点1:本地镜像发布到阿里云流程注意点2:步骤1中本地镜像素材原型注......