ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

c# – PHP的序列化.NET类

2019-08-23 05:32:08  阅读:303  来源: 互联网

标签:php net type-conversion converter c-2


我从源代码中获取了一些序列化的.NET类字符串数据,我只需将其转换为PHP中可读的内容.不一定要变成“对象”或JSON,但我需要以某种方式阅读它.我认为.NET字符串只是一个具有一些设置属性的类,但它是二进制的,显然不可移植.我不打算将.NET代码转换为PHP代码.以下是数据示例:

U:?�S�@��-��v�Y��?������An�@AMAUI������

我意识到这实际上是二进制而不是可打印的文本.我只是用这个作为我在捕获文件时看到的一个例子.

解决方法:

简短回答:

我真的建议不要自己实现二进制表示的解释.我会改用另一种格式(JSON,XML等).

答案很长:

但是,如果不可能,那当然有办法……

实际问题是:序列化.NET对象的二进制格式是什么样的,我们如何正确解释它?

我的所有研究都基于.NET Remoting: Binary Format Data Structure规范.

示例类:

为了有一个工作示例,我创建了一个名为A的简单类,它包含2个属性,一个字符串和一个整数值,它们被称为SomeString和SomeValue.

A类看起来像这样:

[Serializable()]
public class A
{
    public string SomeString
    {
        get;
        set;
    }

    public int SomeValue
    {
        get;
        set;
    }
}

对于序列化,我当然使用了BinaryFormatter:

BinaryFormatter bf = new BinaryFormatter();
StreamWriter sw = new StreamWriter("test.txt");
bf.Serialize(sw.BaseStream, new A() { SomeString = "abc", SomeValue = 123 });
sw.Close();

可以看出,我传递了一个包含abc和123作为值的A类的新实例.

示例结果数据:

如果我们在十六进制编辑器中查看序列化结果,我们会得到这样的结果:

让我们解释一下示例结果数据:

根据上述规范(这里是PDF的直接链接:[MS-NRBF].pdf),流中的每个记录都由RecordTypeEnumeration标识.第2.1.2.1节RecordTypeNumeration状态:

This enumeration identifies the type of the record. Each record (except for MemberPrimitiveUnTyped) starts with a record type enumeration. The size of the enumeration is one BYTE.

SerializationHeaderRecord:

因此,如果我们回顾一下我们得到的数据,我们就可以开始解释第一个字节了:

如2.1.2.1 RecordTypeEnumeration中所述,值为0表示在2.6.1 SerializationHeaderRecord中指定的SerializationHeaderRecord:

The SerializationHeaderRecord record MUST be the first record in a binary serialization. This record has the major and minor version of the format and the IDs of the top object and the headers.

它包括:

> RecordTypeEnum(1个字节)
> RootId(4个字节)
> HeaderId(4个字节)
> MajorVersion(4字节)
> MinorVersion(4个字节)

有了这些知识,我们可以解释包含17个字节的记录:

00表示RecordTypeEnumeration,在我们的例子中是SerializationHeaderRecord.

01 00 00 00表示RootId

If neither the BinaryMethodCall nor BinaryMethodReturn record is present in the serialization stream, the value of this field MUST contain the ObjectId of a Class, Array, or BinaryObjectString record contained in the serialization stream.

所以在我们的例子中,这应该是值为1的ObjectId(因为数据是使用little-endian序列化的),我们希望再次看到它们;-)

FF FF FF FF表示HeaderId

01 00 00 00表示MajorVersion

00 00 00 00表示MinorVersion

BinaryLibrary:

按照规定,每条记录必须以RecordTypeEnumeration开头.当最后一条记录完成时,我们必须假设一条新记录开始.

让我们解释下一个字节:

我们可以看到,在我们的示例中,SerializationHeaderRecord后面是BinaryLibrary记录:

The BinaryLibrary record associates an INT32 ID (as specified in [MS-DTYP] section 2.2.22) with a Library name. This allows other records to reference the Library name by using the ID. This approach reduces the wire size when there are multiple records that reference the same Library name.

它包括:

> RecordTypeEnum(1个字节)
> LibraryId(4个字节)
> LibraryName(可变字节数(这是一个LengthPrefixedString))

如2.1.1.6中所述LengthPrefixedString …

The LengthPrefixedString represents a string value. The string is prefixed by the length of the UTF-8 encoded string in bytes. The length is encoded in a variable-length field with a minimum of 1 byte and a maximum of 5 bytes. To minimize the wire size, length is encoded as a variable-length field.

在我们的简单示例中,长度始终使用1个字节进行编码.有了这些知识,我们可以继续解释流中的字节:

0C表示标识BinaryLibrary记录的RecordTypeEnumeration.

02 00 00 00代表LibraryId,在我们的例子中是2.

现在LengthPrefixedString如下:

42表示包含LibraryName的LengthPrefixedString的长度信息.

在我们的例子中,42(十进制66)的长度信息告诉我们,我们需要读取接下来的66个字节并将它们解释为LibraryName.

如前所述,字符串是UTF-8编码的,因此上面字节的结果类似于:_WorkSpace_,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = null

ClassWithMembersAndTypes:

同样,记录已完成,因此我们解释下一个记录的RecordTypeEnumeration:

05标识ClassWithMembersAndTypes记录.第2.3.2.1节ClassWithMembersAndTypes状态:

The ClassWithMembersAndTypes record is the most verbose of the Class records. It contains metadata about Members, including the names and Remoting Types of the Members. It also contains a Library ID that references the Library Name of the Class.

它包括:

> RecordTypeEnum(1个字节)
> ClassInfo(可变字节数)
> MemberTypeInfo(可变字节数)
> LibraryId(4个字节)

的ClassInfo:

如2.3.1.1 ClassInfo中所述,记录包括:

> ObjectId(4个字节)
>名称(可变字节数(也是LengthPrefixedString))
> MemberCount(4个字节)
> MemberNames(这是一个LengthPrefixedString序列,其中项目数必须等于MemberCount字段中指定的值.)

回到原始数据,一步一步:

01 00 00 00表示ObjectId.我们已经看过这个,它被指定为SerializationHeaderRecord中的RootId.

0F 53 74 61 63 6B 4F 76 65 72 46 6C 6F 77 2E 41表示使用LengthPrefixedString表示的类的名称.如上所述,在我们的示例中,字符串的长度定义为1个字节,因此第一个字节0F指定必须使用UTF-8读取和解码15个字节.结果看起来像这样:StackOverFlow.A – 显然我使用StackOverFlow作为命名空间的名称.

02 00 00 00表示MemberCount,它告诉我们2个成员,两个都用LengthPrefixedString表示.

第一个成员的名字:

1B 3C 53 6F 6D 65 53 74 72 69 6E 67 3E 6B 5F 5F 42 61 63 6B 69 6E 67 46 69 65 6C 64表示第一个MemberName,1B再次是字符串的长度,长度为27个字节,导致像这样:< SomeString> k__BackingField.

第二个成员的名字:

1A 3C 53 6F 6D 65 56 61 6C 75 65 3E 6B 5F 5F 42 61 63 6B 69 6E 67 46 69 65 6C 64表示第二个MemberName,1A指定该字符串长度为26个字节.结果是这样的:< SomeValue> k__BackingField.

MemberTypeInfo:

在ClassInfo之后,遵循MemberTypeInfo.

第2.3.1.2节 – MemberTypeInfo状态,该结构包含:

> BinaryTypeEnums(长度可变)

A sequence of BinaryTypeEnumeration values that represents the Member Types that are being transferred. The Array MUST:

  • Have the same number of items as the MemberNames field of the ClassInfo structure.

  • Be ordered such that the BinaryTypeEnumeration corresponds to the Member name in the MemberNames field of the ClassInfo structure.

> AdditionalInfos(长度可变),取决于BinaryTpeEnum,可能存在也可能不存在其他信息.

| BinaryTypeEnum | AdditionalInfos |
|----------------+--------------------------|
| Primitive | PrimitiveTypeEnumeration |
| String | None |

所以考虑到这一点,我们差不多……
我们期望2个BinaryTypeEnumeration值(因为我们在MemberNames中有2个成员).

再次,回到完整的MemberTypeInfo记录的原始数据:

01表示第一个成员的BinaryTypeEnumeration,根据2.1.2.2 BinaryTypeEnumeration,我们可以期望一个String,并使用LengthPrefixedString表示它.

00表示第二成员的BinaryTypeEnumeration,并且再次,根据规范,它是基元.如上所述,Primitive后面是其他信息,在本例中是PrimitiveTypeEnumeration.这就是为什么我们需要读取下一个字节,即08,与2.1.2.3 PrimitiveTypeEnumeration中所述的表匹配,并惊讶地注意到我们可以期待一个由4个字节表示的Int32,如其他一些文档所述基本数据类型.

库Id:

在LibraryId之后的MemerTypeInfo之后,它由4个字节表示:

02 00 00 00表示LibraryId,即2.

价值:

如2.3类记录中所述:

The values of the Members of the Class MUST be serialized as records that follow this record, as specified in section 2.7. The order of the records MUST match the order of MemberNames as specified in the ClassInfo (section 2.3.1.1) structure.

这就是我们现在可以期待成员价值的原因.

让我们看看最后几个字节:

06标识BinaryObjectString.它表示我们的SomeString属性的值(< SomeString> k__BackingField是准确的).

根据2.5.7 BinaryObjectString,它包含:

> RecordTypeEnum(1个字节)
> ObjectId(4个字节)
>值(可变长度,表示为LengthPrefixedString)

所以我们可以清楚地认识到这一点

03 00 00 00表示ObjectId.

03 61 62 63表示值,其中03是字符串本身的长度,61 62 63是转换为abc的内容字节.

希望你能记得有第二个成员,Int32.知道Int32是用4个字节表示的,我们可以得出结论

必须是我们第二个成员的价值. 7B十六进制等于123十进制,似乎适合我们的示例代码.

所以这是完整的ClassWithMembersAndTypes记录:

MessageEnd:

最后,最后一个字节0B代表MessageEnd记录.

标签:php,net,type-conversion,converter,c-2
来源: https://codeday.me/bug/20190823/1695270.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有