C#中深度解析BinaryFormatter序列化生成的二进制文件

C#中深度解析BinaryFormatter序列化生成的二进制文件

BinaryFormatter序列化时,对象必须有 可序列化特性[Serializable]

一.新建窗体测试程序BinaryDeepAnalysisDemo,将默认的Form1重命名为FormBinaryDeepAnalysis

二.新建测试类Test

Test.cs源程序如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace BinaryDeepAnalysisDemo
{/// <summary>/// 一定要标记该类是可序列化的/// </summary>[Serializable]public class Test{public byte ByteData { get; set; }public short Int16Data { get; set; }public int Id { get; set; }public long Int64Data { get; set; }public float FloatData { get; set; }public double DoubleData { get; set; }public string TestName { get; set; }}
}

三.新建字典与类相互转化类ConversionUtil:

ConversionUtil.cs源程序如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;namespace BinaryDeepAnalysisDemo
{/// <summary>/// 字典Dictionary与自定义类CustomClass属性之间的转化/// 斯内科 2024-05-10/// </summary>public class ConversionUtil{/// <summary>/// 将一个实体类的属性转化为键值对集合,键为属性名PropertyName,值为对应的值/// </summary>/// <typeparam name="T">一种自定义类,该类必须已实例化</typeparam>/// <param name="obj">实例化后的类对象</param>/// <returns>返回转化后的字典</returns>public static Dictionary<string, object> CustomClassToDictionary<T>(T obj) where T : class, new(){Dictionary<string, object> dict = new Dictionary<string, object>();Type type = typeof(T);PropertyInfo[] propertyInfos = type.GetProperties();for (int i = 0; i < propertyInfos.Length; i++){string key = propertyInfos[i].Name;object val = propertyInfos[i].GetValue(obj);dict.Add(key, val);}return dict;}/// <summary>/// 将字典转化为实例化类,如果类的属性名称在字典中存在该键key,就使用该键对应的值为类的属性key赋值【注意,转化可能因值的类型、范围等不同而导致失败】/// 如果类的属性名称在字典中不存在该键key,则不进行赋值,只是按属性对应类型的默认值处理/// </summary>/// <typeparam name="T">要转化的目标类型</typeparam>/// <param name="dict">键值对字典</param>/// <returns>要获取的目标实例化类对象,如果字典为空,则按类的默认值处理</returns>public static T DictionaryToCustomClass<T>(Dictionary<string, object> dict) where T : class{T obj = default(T);if (dict == null || dict.Count == 0){return obj;}Type type = typeof(T);obj = (T)Activator.CreateInstance(type);PropertyInfo[] propertyInfos = type.GetProperties();for (int i = 0; i < propertyInfos.Length; i++){string key = propertyInfos[i].Name;if (dict.ContainsKey(key)){propertyInfos[i].SetValue(obj, dict[key]);}}return obj;}}
}

四.测试窗体设计器相关代码如下

文件FormBinaryDeepAnalysis.Designer.cs


namespace BinaryDeepAnalysisDemo
{partial class FormBinaryDeepAnalysis{/// <summary>/// 必需的设计器变量。/// </summary>private System.ComponentModel.IContainer components = null;/// <summary>/// 清理所有正在使用的资源。/// </summary>/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>protected override void Dispose(bool disposing){if (disposing && (components != null)){components.Dispose();}base.Dispose(disposing);}#region Windows 窗体设计器生成的代码/// <summary>/// 设计器支持所需的方法 - 不要修改/// 使用代码编辑器修改此方法的内容。/// </summary>private void InitializeComponent(){this.panel1 = new System.Windows.Forms.Panel();this.rtxtMessage = new System.Windows.Forms.RichTextBox();this.btnSerial = new System.Windows.Forms.Button();this.btnDeserial = new System.Windows.Forms.Button();this.SuspendLayout();// // panel1// this.panel1.Location = new System.Drawing.Point(12, 12);this.panel1.Name = "panel1";this.panel1.Size = new System.Drawing.Size(300, 532);this.panel1.TabIndex = 0;// // rtxtMessage// this.rtxtMessage.Location = new System.Drawing.Point(511, 12);this.rtxtMessage.Name = "rtxtMessage";this.rtxtMessage.ReadOnly = true;this.rtxtMessage.Size = new System.Drawing.Size(482, 532);this.rtxtMessage.TabIndex = 1;this.rtxtMessage.Text = "";// // btnSerial// this.btnSerial.Location = new System.Drawing.Point(360, 78);this.btnSerial.Name = "btnSerial";this.btnSerial.Size = new System.Drawing.Size(63, 23);this.btnSerial.TabIndex = 2;this.btnSerial.Text = "序列化";this.btnSerial.UseVisualStyleBackColor = true;this.btnSerial.Click += new System.EventHandler(this.btnSerial_Click);// // btnDeserial// this.btnDeserial.Location = new System.Drawing.Point(360, 155);this.btnDeserial.Name = "btnDeserial";this.btnDeserial.Size = new System.Drawing.Size(63, 23);this.btnDeserial.TabIndex = 3;this.btnDeserial.Text = "反序列化";this.btnDeserial.UseVisualStyleBackColor = true;this.btnDeserial.Click += new System.EventHandler(this.btnDeserial_Click);// // FormBinaryDeepAnalysis// this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;this.ClientSize = new System.Drawing.Size(1001, 556);this.Controls.Add(this.btnDeserial);this.Controls.Add(this.btnSerial);this.Controls.Add(this.rtxtMessage);this.Controls.Add(this.panel1);this.Name = "FormBinaryDeepAnalysis";this.Text = "序列化生成的二进制进行深度解析";this.Load += new System.EventHandler(this.FormBinaryDeepAnalysis_Load);this.ResumeLayout(false);}#endregionprivate System.Windows.Forms.Panel panel1;private System.Windows.Forms.RichTextBox rtxtMessage;private System.Windows.Forms.Button btnSerial;private System.Windows.Forms.Button btnDeserial;}
}

五.窗体FormBinaryDeepAnalysis相关示例代码如下

文件FormBinaryDeepAnalysis.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;namespace BinaryDeepAnalysisDemo
{public partial class FormBinaryDeepAnalysis : Form{public FormBinaryDeepAnalysis(){InitializeComponent();}/// <summary>/// 显示文本/// </summary>/// <param name="contents"></param>private void DisplayMessage(string contents) {if (!IsHandleCreated)return;this.BeginInvoke(new Action(() =>{if (rtxtMessage.TextLength >= 20480) {rtxtMessage.Clear();}rtxtMessage.AppendText($"{contents}\n");rtxtMessage.ScrollToCaret();}));}private void FormBinaryDeepAnalysis_Load(object sender, EventArgs e){Test test = new Test(){ByteData = 234,Int16Data = 23456,Id = 12345678,Int64Data = 9876543210123L,FloatData = 60.25F,DoubleData = -87654321.3125,TestName = "ABC123abc"};Dictionary<string, object> dict = ConversionUtil.CustomClassToDictionary(test);for (int i = 0; i < dict.Count; i++){KeyValuePair<string, object> keyValuePair = dict.ElementAt(i);Label label = new Label();label.Name = $"lbl{keyValuePair.Key}";label.Text = keyValuePair.Key;label.Location = new Point(3, 3 + 30 * i);TextBox textBox = new TextBox();textBox.Name = $"txb{keyValuePair.Key}";textBox.Text = Convert.ToString(keyValuePair.Value);textBox.Location = new Point(100, 3 + 30 * i);textBox.Size = new Size(160, 25);textBox.Tag = keyValuePair.Key;panel1.Controls.AddRange(new Control[] { label, textBox });}}private void btnSerial_Click(object sender, EventArgs e){try{Dictionary<string, object> dict = new Dictionary<string, object>();for (int i = 0; i < panel1.Controls.Count; i++){Control control = panel1.Controls[i];if (control is TextBox){object val = control.Text;switch (Convert.ToString(control.Tag)){case "ByteData":val = byte.Parse(control.Text);break;case "Int16Data":val = short.Parse(control.Text);break;case "Id":val = int.Parse(control.Text);break;case "Int64Data":val = long.Parse(control.Text);break;case "FloatData":val = float.Parse(control.Text);break;case "DoubleData":val = double.Parse(control.Text);break;}dict.Add(Convert.ToString(control.Tag), val);}}Test test = ConversionUtil.DictionaryToCustomClass<Test>(dict);System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();string path = $"{AppDomain.CurrentDomain.BaseDirectory}test.bin";using (System.IO.FileStream stream = new System.IO.FileStream(path, System.IO.FileMode.Create)){binaryFormatter.Serialize(stream, test);}byte[] buffer = System.IO.File.ReadAllBytes(path);DisplayMessage($"长度:{buffer.Length}\n字节流:\n{string.Join(",", buffer)}");DisplayMessage("------打印每个属性的字节流编码,以及查找序列化的二进制数据------");SeekData(TypeCode.Byte, "ByteData", test.ByteData, buffer);SeekData(TypeCode.Int16, "Int16Data", test.Int16Data, buffer);SeekData(TypeCode.Int32, "Id", test.Id, buffer);SeekData(TypeCode.Int64, "Int64Data", test.Int64Data, buffer);SeekData(TypeCode.Single, "FloatData", test.FloatData, buffer);SeekData(TypeCode.Double, "DoubleData", test.DoubleData, buffer);SeekData(TypeCode.String, "TestName", test.TestName, buffer);DisplayMessage("------打印可转换为ASCII的字符,如果是看不到的特殊字符,显示源字节------");string ascii = "";for (int i = 0; i < buffer.Length; i++){if (buffer[i] >= 32 && buffer[i] <= 126){ascii += (char)buffer[i];}else{ascii += $"【{buffer[i]}】";}}DisplayMessage(ascii);}catch (Exception ex) {DisplayMessage($"序列化时出现异常:【{ex.Message}】");}}/// <summary>/// 从字节流中查找数据/// </summary>/// <param name="typeCode"></param>/// <param name="propertyName"></param>/// <param name="val"></param>/// <param name="buffer"></param>private void SeekData(TypeCode typeCode, string propertyName, object val, byte[] buffer) {int byteCount = 0;//类型所占用的字节个数byte[] dataBuffer = new byte[0];switch (typeCode){case TypeCode.Byte:byteCount = sizeof(byte);//类型所占用的字节个数dataBuffer = new byte[] { (byte)val };break;case TypeCode.Int16:byteCount = sizeof(short);dataBuffer = BitConverter.GetBytes((short)val);break;case TypeCode.Int32:byteCount = sizeof(int);dataBuffer = BitConverter.GetBytes((int)val);break;case TypeCode.Int64:byteCount = sizeof(long);dataBuffer = BitConverter.GetBytes((long)val);break;case TypeCode.Single:byteCount = sizeof(float);dataBuffer = BitConverter.GetBytes((float)val);break;case TypeCode.Double:byteCount = sizeof(double);dataBuffer = BitConverter.GetBytes((double)val);break;default:dataBuffer = Encoding.ASCII.GetBytes(Convert.ToString(val));byteCount = dataBuffer.Length;break;}DisplayMessage($"{propertyName}属性的值为【{val}】,字节流【{string.Join(",", dataBuffer)}】");List<int> listIndex = new List<int>();for (int i = 0; i < buffer.Length - byteCount; i++){bool[] bArray = new bool[byteCount];for (int cnt = 0; cnt < byteCount; cnt++){bArray[cnt] = buffer[i + cnt] == dataBuffer[cnt];}//查找所有元素是否都为trueif (Array.FindAll(bArray, x => x).Length == byteCount) {listIndex.Add(i);}}DisplayMessage($"\x20\x20\x20【{propertyName}】数据的地址查找到的起始索引有【{string.Join(",", listIndex)}】");}private void btnDeserial_Click(object sender, EventArgs e){Test test = null;string path = $"{AppDomain.CurrentDomain.BaseDirectory}test.bin";System.Runtime.Serialization.Formatters.Binary.BinaryFormatter binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();using (System.IO.FileStream stream = new System.IO.FileStream(path, System.IO.FileMode.Open)){test = binaryFormatter.Deserialize(stream) as Test;}if(test == null) {return;}Dictionary<string, object> dict = ConversionUtil.CustomClassToDictionary(test);for (int i = 0; i < panel1.Controls.Count; i++){Control control = panel1.Controls[i];if (control is TextBox){control.Text = "";switch (Convert.ToString(control.Tag)){case "ByteData":control.Text = Convert.ToString(test.ByteData);break;case "Int16Data":control.Text = Convert.ToString(test.Int16Data);break;case "Id":control.Text = Convert.ToString(test.Id);break;case "Int64Data":control.Text = Convert.ToString(test.Int64Data);break;case "FloatData":control.Text = Convert.ToString(test.FloatData);break;case "DoubleData":control.Text = Convert.ToString(test.DoubleData);break;default:control.Text = test.TestName;break;}}}}}
}

六.窗体运行如图:

我们可以发现 生成的字节码中 含有 程序集和类信息,以及属性名和对应的值

程序集和版本信息

类和属性字段以及相关数据类型信息

<TestName> 这里的TestName就是属性字段,如果需要获取该字段的类型,可以使用System.Reflection反射来获取具体的数据类型

具体数值区域

这里的数据大多都是使用BitConverter.GetBytes(数据值)进行字节流转化

七.BinaryFormatter序列化的不安全性

 如果将二进制文件读取出来,然后篡改指定的字节码,生成新的二进制文件,然后重新读取,结果就会发现数据被篡改了.很多单机游戏的存档文件修改就是这么干的.

这也是不提倡使用BinaryFormatter类进行序列化的原因,可能被劫持篡改二进制文件,因此在最新的net 5.0中会被标记为不安全的和过时的原因

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

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

相关文章

【实用教程】在 Android Studio 中连接 MuMu 模拟器

MuMu 模拟器是一个非常流行的安卓模拟器&#xff0c;特别适合开发人员进行应用测试&#xff0c;我使用它的根本原因在于Android Studio自带的AVM实现是太难用了&#xff0c;但是Mumu模拟器启动以后不会自动被Android Studio识别到&#xff0c;但是其他模拟器都是能够正常被Andr…

LLAMA-Factory安装教程(解决报错cannot allocate memory in static TLS block的问题)

步骤一&#xff1a; 下载基础镜像 # 配置docker DNS vi /etc/docker/daemon.json # daemon.json文件中 { "insecure-registries": ["https://swr.cn-east-317.qdrgznjszx.com"], "registry-mirrors": ["https://docker.mirrors.ustc.edu.c…

Ollama 部署 DeepSeek-R1 及Open-WebUI

Ollama 部署 DeepSeek-R1 及Open-WebUI 文章目录 Ollama 部署 DeepSeek-R1 及Open-WebUI〇、说明为什么使用本方案 一、 安装Ollama1、主要特点&#xff1a;2、安装3、验证 二、Ollama 部署 DeepSeek1、部署2、模型选用3、Ollama 常用命令4、Ollama模型默认存储路径 安装open-w…

基于微信小程序的医院预约挂号系统的设计与实现

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…

redis项目

短信登录 这一块我们会使用redis共享session来实现 商户查询缓存 通过本章节&#xff0c;我们会理解缓存击穿&#xff0c;缓存穿透&#xff0c;缓存雪崩等问题&#xff0c;让小伙伴的对于这些概念的理解不仅仅是停留在概念上&#xff0c;更是能在代码中看到对应的内容 优惠…

【嵌入式 Linux 音视频+ AI 实战项目】瑞芯微 Rockchip 系列 RK3588-基于深度学习的人脸门禁+ IPC 智能安防监控系统

前言 本文主要介绍我最近开发的一个个人实战项目&#xff0c;“基于深度学习的人脸门禁 IPC 智能安防监控系统”&#xff0c;全程满帧流畅运行。这个项目我目前全网搜了一圈&#xff0c;还没发现有相关类型的开源项目。这个项目只要稍微改进下&#xff0c;就可以变成市面上目前…

RabbitMQ 从入门到精通:从工作模式到集群部署实战(四)

#作者&#xff1a;闫乾苓 系列前几篇&#xff1a; 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;一&#xff09;》&#xff1a;link 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;二&#xff09;》&#xff1a; lin…

RabbitMQ 从入门到精通:从工作模式到集群部署实战(五)

#作者&#xff1a;闫乾苓 系列前几篇&#xff1a; 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;一&#xff09;》&#xff1a;link 《RabbitMQ 从入门到精通&#xff1a;从工作模式到集群部署实战&#xff08;二&#xff09;》&#xff1a; lin…

mysql 学习11 事务,事务简介,事务操作,事务四大特性,并发事务问题,事务隔离级别

一 事务简介&#xff0c; 数据库准备&#xff1a; create table account(id int auto_increment primary key comment 主键ID,name varchar(128) not null comment 姓名,backaccountnumber char(18) unique comment 银行账号,money float comment 余额 )comment 银行账号表;…

C语言的灵魂——指针(3)

前言&#xff1a;上期我们介绍了const修饰指针&#xff0c;saaert断言都是针对指针本身的&#xff0c;文章后面我们用指针与数组建立了联系&#xff0c;这种联系或者是关系就是这篇文章所要介绍的。上一篇文章的传送门&#xff1a;指针2 指针3 一&#xff0c;数组名的含义及理解…

企业FTP替代升级,实现传输大文件提升100倍!

随着信息技术的飞速发展&#xff0c;网络安全环境也变得越来越复杂。在这种背景下&#xff0c;传统的FTP&#xff08;文件传输协议&#xff09;已经很难满足现代企业对文件传输的需求了。FTP虽然用起来简单&#xff0c;但它的局限性和安全漏洞让它在面对高效、安全的数据交换时…

树和二叉树_7

树和二叉树_7 一、leetcode-102二、题解1.引库2.代码 一、leetcode-102 二叉树的层序遍历 给你二叉树的根节点 root &#xff0c;返回其节点值的 层序遍历 。 &#xff08;即逐层地&#xff0c;从左到右访问所有节点&#xff09;。 样例输入&#xff1a;root [3,9,20,null,nu…

2.8作业

作业 优化登录框&#xff1a; 当用户点击取消按钮&#xff0c;弹出问题对话框&#xff0c;询问是否要确定退出登录&#xff0c;并提供两个按钮&#xff0c;yes|No&#xff0c;如果用户点击的Yes&#xff0c;则关闭对话框&#xff0c;如果用户点击的No&#xff0c;则继续登录 当…

【WB 深度学习实验管理】使用 PyTorch Lightning 实现高效的图像分类实验跟踪

本文使用到的 Jupyter Notebook 可在GitHub仓库002文件夹找到&#xff0c;别忘了给仓库点个小心心~~~ https://github.com/LFF8888/FF-Studio-Resources 在机器学习项目中&#xff0c;实验跟踪和结果可视化是至关重要的环节。无论是调整超参数、优化模型架构&#xff0c;还是监…

人工智能入门 数学基础 线性代数 笔记

必备的数学知识是理解人工智能不可或缺的要素&#xff0c;今天的种种人工智能技术归根到底都建立在数学模型之上&#xff0c;而这些数学模型又都离不开线性代数&#xff08;linear algebra&#xff09;的理论框架。 线性代数的核心意义&#xff1a;世间万事万物都可以被抽象成某…

5 计算机网络

5 计算机网络 5.1 OSI/RM七层模型 5.2 TCP/IP协议簇 5.2.1:常见协议基础 一、 TCP是可靠的&#xff0c;效率低的&#xff1b; 1.HTTP协议端口默认80&#xff0c;HTTPSSL之后成为HTTPS协议默认端口443。 2.对于0~1023一般是默认的公共端口不需要注册&#xff0c;1024以后的则需…

unity碰撞的监测和监听

1.创建一个地面 2.去资源商店下载一个火焰素材 3.把procedural fire导入到自己的项目包管理器中 4.给magic fire 0 挂在碰撞组件Rigidbody , Sphere Collider 5.创建脚本test 并挂在magic fire 0 脚本代码 using System.Collections; using System.Collections.Generic; usi…

使用云效解决docker官方镜像拉取不到的问题

目录 前言原文地址测试jenkins构建结果:后续使用说明 前言 最近经常出现docker镜像进行拉取不了&#xff0c;流水线挂掉的问题&#xff0c;看到一个解决方案: 《借助阿里个人版镜像仓库云效实现全免费同步docker官方镜像到国内》 原文地址 https://developer.aliyun.com/artic…

element-plus+vue3前端如何根据name进行搜索查到符合条件的数据

界面如图&#xff0c;下面的区域是接口给的所有的&#xff0c;希望前端根据输入的内容自己去匹配。 我是使用的element-plusvue3ts的写法。 <el-input v-model"filters.region" placeholder"输入区域搜索" keyup"filterRegion(filters.region)&q…

电路研究9.3——合宙Air780EP中的AT开发指南(含TCP 示例)

根据合宙的AT研发推荐&#xff0c; AT指令基本上也简单看完了&#xff0c;这里开始转到AT的开发了。 AT 命令采用标准串口进行数据收发&#xff0c;将以前复杂的设备通讯方式转换成简单的串口编程&#xff0c; 大大简化了产品的硬件设计和软件开发成本&#xff0c;这使得几乎所…