首页 > 其他分享 >[dotnet-Sec]初探反序列化

[dotnet-Sec]初探反序列化

时间:2024-02-22 14:11:46浏览次数:33  
标签:info Console Sec WriteLine new dotnet StreamingContext 序列化

[dotnet-Sec]初探反序列化

参考Github上y4✌的开源笔记,狠狠学!

环境搭建

.NET:5.0

IDE:Rider(JB家族)

新建项目

选择.NET Core(支持跨平台)下的控制台应用程序,然后创建

image-20240221163113770

这是接触到的关于dotnet的第一个反序列化demo,使用的是BinaryFormatter生成二进制流

// Disable the warning.
#pragma warning disable SYSLIB0011

using System.ComponentModel.DataAnnotations;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace dotnet_sec_101
{
    [Serializable]
    public class DemoObject
    {
        public int n1;
        [NonSerialized] public int n2;
        public string str;
    }

    class Tester
    {
        public static void BinaryFomatterSerialize(string file, object o)
        {
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            FileStream fileStream = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.None);
            binaryFormatter.Serialize(fileStream, o);
            fileStream.Close();
            Console.WriteLine($"serialize object {o} to file {file}.");
        }


        public static object BinaryFomatterDeserialFromFile(string file)
        {
            IFormatter formatter = new BinaryFormatter();
            Stream stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
            object o = formatter.Deserialize(stream);
            stream.Close();
            return o;
        }

        static void Main(string[] args)
        {
            try
            {
                DemoObject demoObject = new DemoObject();
                demoObject.n1 = 1;
                demoObject.n2 = 2;
                demoObject.str = "hack";
                
                BinaryFomatterSerialize("ser.bin", demoObject);
                DemoObject _demoObject = (DemoObject)BinaryFomatterDeserialFromFile("ser.bin");
                
                Console.WriteLine($"n1: {_demoObject.n1}");
                Console.WriteLine($"NonSer n2: {_demoObject.n2}");
                Console.WriteLine($"str: {_demoObject.str}");
            }
            catch (Exception e) {
                Console.WriteLine(e.Message);
            }
            
            Console.ReadKey();
    
        }
    
    }
}
// Re-enable the warning.
#pragma warning restore SYSLIB0011

由于.net的高版本问题,需要在csproj项目文件中添加如下条目

image-20240221163800130

运行,可以发现加了[NonSerialized]特性的变量n2未参与序列化,于是在反序列化后也就没有对应值,默认为0

image-20240221163827075

我们查看使用BinaryFormatter生成的二进制文件ser.bin,开头为00 01 00 00

image-20240221164027350

IFormatter接口

我们查看底层BinaryFormatter的实现,实现了IFormatter接口

(后面测试在.net framework平台下还继承了另一个接口IRemotingFormatter

IRemotingFormatter是用于远程调用的RPC接口

image-20240221224056030

image-20240221205733348

最终的IFormatter接口如下:

  public interface IFormatter
  {
    object Deserialize(Stream serializationStream);

    void Serialize(Stream serializationStream, object graph);

    ISurrogateSelector SurrogateSelector { get; set; }

    SerializationBinder Binder { get; set; }		

    StreamingContext Context { get; set; }
  }

IFormatter定义了序列化和反序列化的两个方法,以及三个字段,其中每个字段含义如下:

类 字段名 含义用途
ISurrogateSelector SurrogateSelector 序列化代理选择器 接管formatter的序列化或反序列化处理
SerializationBinder Binder 用于控制在序列化和反序列化期间使用的实际类型
StreamingContext Context 序列化流上下文 其中states字段包含了序列化的来源和目的地

通过这三个字段,我们可以控制序列化和反序列化时数据的类型、值以及其他信息。

序列化和反序列化的生命周期

生命周期如下:

  1. 首先确定formatter是否有代理选择器,如果有则检查代理选择器要处理的对象类型是否和给定的对象类型一致,如果一致,代理选择器会调用ISerializable.GetObjectData()
  2. 如果没有代理选择器,或者代理选择器不处理该对象类型,则检查对象是否有[Serializable]特性。如果不能序列化则抛出异常。
  3. 检查该对象是否实现ISerializable接口,如果实现就调用其GetObjectData方法。
  4. 如果没实现ISerializable接口就使用默认的序列化策略,序列化所有没标记[NonSerialized]的字段。

image-20240222140227018

ISerializationSurrogate代理器模式

首先看下它的接口定义:

需要在继承接口中实现如下函数:GetObjectDataSetObjectData

using System.Runtime.InteropServices;
using System.Security;

#nullable disable
namespace System.Runtime.Serialization
{
  [ComVisible(true)]
  public interface ISerializationSurrogate
  {
    [SecurityCritical] // 确保只有具有足够权限的代码才能调用或访问被标记的代码。
    void GetObjectData(object obj, SerializationInfo info, StreamingContext context);
      // 序列化的时候被调用(压缩对象前需要get data)
      

    [SecurityCritical]
    object SetObjectData(		// 反序列化时调用(恢复对象时要set data)
      object obj,
      SerializationInfo info,
      StreamingContext context,
      ISurrogateSelector selector);
  }
}

image-20240222114037185

非代理器 + 继承ISerializable

接口定义如下:

#nullable disable
namespace System.Runtime.Serialization
{
  [ComVisible(true)]
  public interface ISerializable
  {
    [SecurityCritical]
    void GetObjectData(SerializationInfo info, StreamingContext context);
  }
}

注释掉BinaryFormatter设置代理器的语句即可

image-20240222113742872

然后运行可以发现

image-20240222113958495

注解模式(使用四个回调事件)

这个没啥好说的

特性 调用关联的方法时 典型用法
OnDeserializingAttribute 反序列化之前 初始化可选字段的默认值。
OnDeserializedAttribute 反序列化之后 根据其他字段的内容修改可选字段值。
OnSerializingAttribute 序列化之前 准备序列化。 例如,创建可选数据结构。
OnSerializedAttribute 序列化之后 记录序列化事件。

演示demo

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security.Permissions;


namespace NetSerializer
{
    [Serializable]
    public class MyObject : ISerializable
    {
        public string str { get; set; }

        public MyObject()
        {
            
        }

        // 实现Iserializable接口的类必须包含有序列化构造函数, 否则会出错
        protected MyObject(SerializationInfo info, StreamingContext context)
        {
            Console.WriteLine("MyObject(SerializationInfo info, StreamingContext context)");
            str = info.GetString("str");
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            Console.WriteLine("GetObjectData(SerializationInfo info, StreamingContext context)");
            info.AddValue("str", str, typeof(string));
        }
        
        // 四个回调事件 以及对应的特性
        [OnDeserializing] // 反序列化之前
        private void TestForOnDeserializing(StreamingContext sc)
        {
            // 打印回调函数调用时的上下文
            //Console.WriteLine(sc.ToString());
            Console.WriteLine("OnDeserializing Test");
        }

        [OnDeserialized]
        private void TestForOnDeserialized(StreamingContext sc)
        {
            //Console.WriteLine(sc.ToString());
            Console.WriteLine("OnDeserialized Test");
        }

        [OnSerializing]
        private void TestForOnSerializing(StreamingContext sc)
        {
            //Console.WriteLine(sc.ToString());
            Console.WriteLine("OnSerializing Test");
        }

        [OnSerialized]
        private void TestForOnSerialized(StreamingContext sc)
        {
            //Console.WriteLine(sc.ToString());
            Console.WriteLine("OnSerialized Test");
        }
        
    }

    
    
    // 声明代理选择器
    class MySerializationSurrogate : ISerializationSurrogate
    {
        public void GetObjectData(object obj, SerializationInfo info, StreamingContext context)
        {
            Console.WriteLine("GetObjectData of IserializationSurrogate");
            info.AddValue("str", ((MyObject)obj).str);
        }


        public object SetObjectData(object obj, SerializationInfo info, StreamingContext context,
            ISurrogateSelector selector)
        {
            Console.WriteLine("SetObjectData of IserializationSurrogate");
            MyObject m = new MyObject();
            m.str = (string)info.GetValue("str", typeof(string));
            return m;
        }

    }


    class Tester
    {
        static void Main(string[] args)
        {
            try
            {
                MyObject myObject = new MyObject();
                myObject.str = "hello";

                using (MemoryStream memoryStream = new MemoryStream())
                {
                    // 构建formatter
                    BinaryFormatter binaryFormatter = new BinaryFormatter();
                    
                    // 设置序列化代理选择器
                    SurrogateSelector ss = new SurrogateSelector();
                    ss.AddSurrogate(typeof(MyObject), binaryFormatter.Context, new MySerializationSurrogate());

                    // 将序列化代理选择器赋给formatter 
                    binaryFormatter.SurrogateSelector = ss;
                    
                    // 序列化
                    binaryFormatter.Serialize(memoryStream, myObject);
                    
                    // 重置stream
                    memoryStream.Position = 0;
                    myObject = null;
                    
                    // 反序列化
                    myObject = (MyObject)binaryFormatter.Deserialize(memoryStream);
                    Console.WriteLine(myObject.str);
                }
                
                

            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
                      
        }
    }
}

生命周期小结

image-20210420105228965

SerializationInfo

在上面生命周期的探讨中,使用到了SerializationInfo作为参数

存储序列化过程中的信息

image-20240222130807788

其中上述的AddValue函数的底层实现:

使用m_membersm_datam_types存储变量名、值、类型

    internal void AddValueInternal(string name, object value, Type type)
    {
      if (this.m_nameToIndex.ContainsKey(name))
        throw new SerializationException(Environment.GetResourceString("Serialization_SameNameTwice"));
      this.m_nameToIndex.Add(name, this.m_currMember);
      if (this.m_currMember >= this.m_members.Length)
        this.ExpandArrays();
      this.m_members[this.m_currMember] = name;
      this.m_data[this.m_currMember] = value;
      this.m_types[this.m_currMember] = type;
      ++this.m_currMember;
    }

​ 在不使用代理器模式的情况且在反序列化的过程中,通过调试可以发现,在CompleteISerializableObject() 中调用了runtimeConstructorInfo.SerializationInvoke(obj, info, context),这也能说明了SerializationInfo的作用

image-20240222134147131

​ 然后进入到自定义的构造函数,完整栈帧如下

Main Thread
ObjectManager.CompleteISerializableObject()
ObjectManager.FixupSpecialObject()
ObjectManager.DoFixups()
ObjectReader.Deserialize()
BinaryFormatter.Deserialize()
BinaryFormatter.Deserialize()
Tester.Main()

internal void CompleteISerializableObject(
  object obj,
  SerializationInfo info,
  StreamingContext context)
{
  if (obj == null)
    throw new ArgumentNullException(nameof (obj));
  RuntimeType t = obj is ISerializable ? (RuntimeType) obj.GetType() : throw new ArgumentException(Environment.GetResourceString("Serialization_NotISer"));
  RuntimeConstructorInfo runtimeConstructorInfo;
  try
  {
    runtimeConstructorInfo = !(t == ObjectManager.TypeOfWindowsIdentity) || !this.m_isCrossAppDomain ? ObjectManager.GetConstructor(t) : WindowsIdentity.GetSpecialSerializationCtor();
  }
  catch (Exception ex)
  {
    throw new SerializationException(Environment.GetResourceString("Serialization_ConstructorNotFound", (object) t), ex);
  }
    // 调用构造函数
  runtimeConstructorInfo.SerializationInvoke(obj, info, context);
}

​ 相应的在序列化过程中可以找到调用GetObjectData的时机:

Main Thread
WriteObjectInfo.InitSerialize()
WriteObjectInfo.Serialize()
ObjectWriter.Serialize()
BinaryFormatter.Serialize()
BinaryFormatter.Serialize()
Tester.Main()

image-20240222140013506

标签:info,Console,Sec,WriteLine,new,dotnet,StreamingContext,序列化
From: https://www.cnblogs.com/icfh/p/18027206

相关文章

  • secueCRT脚本小试牛刀
    secureCRT可以使用脚本自动化执行操作,如登录脚本。支持vbs、python(目前最高仅3.9)和jscript。据说还可以录制操作,没试过。secureCRT版本为9.10。secureCRT内置了一些对象,入crt,具体用法可见官网,或其他网上资料。尝试了vbs和python,功能一样:#$language="VBScript"#......
  • Weblogic XMLDecoder反序列化漏洞(CVE-2017-10271)复现
    0x00漏洞简介OracleFusionMiddleware(Oracle融合中间件)是美国甲骨文(Oracle)公司的一套面向企业和云环境的业务创新平台。该平台提供了中间件、软件集合等功能。OracleWebLogicServer是其中的一个适用于云环境和传统环境的应用服务器组件。OracleFusionMiddleware中的Oracle......
  • Apache Shiro反序列化漏洞 (CVE-2016-4437)复现
    0x00漏洞简介ApacheShiro是一款开源安全框架,提供身份验证、授权、密码学和会话管理。Shiro框架直观、易用,同时也能提供健壮的安全性。ApacheShiro1.2.4及以前版本中,加密的用户信息序列化后存储在名为remember-me的Cookie中。攻击者可以使用Shiro的默认密钥伪造用户Cookie,触......
  • python实战:使用json序列化
    一,官方文档:https://docs.python.org/zh-cn/3/library/json.html二,json与字典的相互转化1,字典转json字符串1234567importjson #字典转jsond=dict(name='Tom',age=2,score=88)json_d=json.dumps(d)print(type(json_d))print(json_d)......
  • SpringSecurity与JWT如何实现项目端分离认证与授权
    ✅SpringSecurity+JWT实现项目前端分离认证授权✅1.简介SpringSecurity是Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,杜区资源也比Shiro丰富。一般来说中大型的项目都是使用SpringSecurity来做安全框架。小项目有Shiro的比较多,因......
  • rust结构体包含另一个结构体引用时,serde序列化问题
    代码如下useserde::{Deserialize,Serialize};#[derive(Serialize,Deserialize)]structPerson{id:String,name:String,}#[derive(Serialize,Deserialize)]structMsg<'a>{id:String,person:&'aPerson,}fnmain(){......
  • 洛谷题单指南-递推与递归-P3612 [USACO17JAN] Secret Cow Code S
    原题链接:https://www.luogu.com.cn/problem/P3612题意解读:字符串加长的时候,是先把最后一个字符接上,再拼接其余字符,注意不是翻转,要找第n个字符,就要看字符串加长几次后长度能超过n,然后在加长后的字符串中找第n个字符。解题思路:如果直接通过模拟法,字符串长度太长,且要找的第n个数......
  • 可视化视频监控平台EasyCVR如何配置服务参数以免getbaseconfig接口信息泄露?
    可视化云监控平台/安防视频监控系统EasyCVR视频综合管理平台,采用了开放式的网络结构,平台支持高清视频的接入和传输、分发,可以提供实时远程视频监控、视频录像、录像回放与存储、告警、语音对讲、云台控制、平台级联、磁盘阵列存储、视频集中存储、云存储等丰富的视频能力,同时还具......
  • FileZilla 服务器 报Warning: FTP over TLS is not enabled, users cannot securely l
    FileZilla服务器报Warning:FTPoverTLSisnotenabled,userscannotsecurelylogin.1.登录至FTP服务器 2.选择编辑->设置->SSL/TLS设置->。。。。。[看图操作],注:证书导出路径不能有中文字符 3.选择编辑->设置->SSL/TLS设置->选择上一步操作导出的证书,注意导出......
  • Gartner® Market Guide for Email Security——Gartner对邮件安全市场的洞察
    https://www.gartner.com/doc/reprints?id=1-2DVHQRLR&ct=230531&st=sb看最关键的,邮件安全厂商,三大类:Acompaniontoolisalsoavailablethatincludesalargersetof42representativevendorsandtheircapabilities(see Tool:VendorIdentificationforEmailSecur......