An entry with the mimetype application/x-microsoft.net.object.binary.base64 holds the output of BinaryFormatter in base64. The stream is the .NET Remoting Binary Format, documented as MS-NRBF. It is a sequence of records; each starts with a one-byte record type. Integers are little-endian; a length-prefixed string is a length in 7-bit groups (the low seven bits first, the high bit set when more follow) and then that many bytes of UTF-8.
The stream
- A
SerializedStreamHeaderrecord:RootId(the id of the root object),HeaderId(-1),MajorVersion(1),MinorVersion(0), each a 32-bit integer. - Records for the objects, in the order they are first written:
BinaryLibraryrecords that give each assembly a number, class records, strings, arrays. - A
MessageEndrecord (type 11).
Every object has a 32-bit id (positive); a value that is an object already written is a MemberReference to its id, so the stream is a graph and may contain cycles.
Record types
| Code | Record | Layout |
|---|---|---|
| 0 | SerializedStreamHeader | RootId, HeaderId, MajorVersion, MinorVersion (4 × Int32) |
| 1 | ClassWithId | ObjectId, MetadataId (the id of an earlier class record whose layout is reused); then the member values |
| 2 | SystemClassWithMembers | ClassInfo (ObjectId, Name, MemberCount, MemberNames); then the member values (a system class, no member types) |
| 3 | ClassWithMembers | ClassInfo, LibraryId; then the member values (no member types) |
| 4 | SystemClassWithMembersAndTypes | ClassInfo, MemberTypeInfo; then the member values |
| 5 | ClassWithMembersAndTypes | ClassInfo, MemberTypeInfo, LibraryId; then the member values |
| 6 | BinaryObjectString | ObjectId, Value (a length-prefixed string) |
| 7 | BinaryArray | ObjectId, ArrayType (0 Single, 1 Jagged, 2 Rectangular, 3 SingleOffset, 4 JaggedOffset, 5 RectangularOffset), Rank, Lengths (Rank × Int32), LowerBounds (Rank × Int32, only for the offset types), the element’s type (a member type code and its additional information); then the elements |
| 8 | MemberPrimitiveTyped | PrimitiveType, Value |
| 9 | MemberReference | IdRef |
| 10 | ObjectNull | — |
| 11 | MessageEnd | — |
| 12 | BinaryLibrary | LibraryId, LibraryName (a length-prefixed string, the full assembly name) |
| 13 | ObjectNullMultiple256 | NullCount (one byte): a run of nulls |
| 14 | ObjectNullMultiple | NullCount (Int32): a run of nulls |
| 15 | ArraySinglePrimitive | ObjectId, Length, PrimitiveType; then the raw values |
| 16 | ArraySingleObject | ObjectId, Length; then the elements as records |
| 17 | ArraySingleString | ObjectId, Length; then the elements as records (strings, references, nulls) |
| 21, 22 | MethodCall, MethodReturn | remoting messages; they do not occur in a resource file and the viewer stops at them |
Member type codes
| Code | Type | Additional information |
|---|---|---|
| 0 | Primitive | the primitive type code (a byte) |
| 1 | String | — |
| 2 | Object | — |
| 3 | SystemClass | the class name (a length-prefixed string) |
| 4 | Class | the class name and a LibraryId (Int32) |
| 5 | ObjectArray | — |
| 6 | StringArray | — |
| 7 | PrimitiveArray | the primitive type code of the elements |
In MemberTypeInfo the member type codes of all the members come first (one byte each), then the additional information of those that have some, in the same order. A primitive member is written as its raw value, with no record around it; every other member is written as a record (a string, a reference, null, a nested class, an array).
Primitive type codes
| Code | Type | Bytes | Code | Type | Bytes |
|---|---|---|---|---|---|
| 1 | Boolean | 1 | 10 | SByte | 1 |
| 2 | Byte | 1 | 11 | Single | 4 |
| 3 | Char | 1 to 4 (UTF-8) | 12 | TimeSpan | 8 (ticks) |
| 5 | Decimal | a length-prefixed string | 13 | DateTime | 8 (ticks in 62 bits, kind in 2) |
| 6 | Double | 8 | 14 | UInt16 | 2 |
| 7 | Int16 | 2 | 15 | UInt32 | 4 |
| 8 | Int32 | 4 | 16 | UInt64 | 8 |
| 9 | Int64 | 8 | 17 | Null | 0 |
| 18 | String | a length-prefixed string |
What a resource file usually holds
00 SerializedStreamHeader
01000000 FFFFFFFF 01000000 00000000 root 1, header -1, version 1.0
0C 02000000 "System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
BinaryLibrary: library 2
05 ClassWithMembersAndTypes
01000000 "System.Drawing.Bitmap" 01000000 "Data" object 1, one member, named Data
07 02 member type: PrimitiveArray of Byte (2)
02000000 LibraryId 2
0F 03000000 (length) 02 ArraySinglePrimitive of Byte: object 3, the PNG bytes
89504E47 …
0B MessageEnd
System.Drawing.Bitmap: a library, a class with one member (Data, a byte array) and the bytes of the picture file. An Icon has the member IconData; a System.Windows.Forms.ImageListStreamer has Data with the image list in the form the Windows common controls write (it starts with MSFt).The same layout carries other serialised objects: a string (BinaryObjectString), a list or dictionary (a class with members such as _items and _size), a System.Windows.Forms.AxHost.State (the persisted state of an ActiveX control), and application types. The viewer lists the class and its members for any of them, and shows byte arrays with their size and kind.
Safety
Deserialising a BinaryFormatter stream can create any type the stream names, which is why BinaryFormatter is no longer recommended and is disabled in current .NET. The viewer never deserialises: it reads the records, shows what is written, and stops at the limits (2,000,000 records, depth 200). It does not follow a class name to code.
Not described
Known, not yet covered
- Reference: the remoting call records (21 and 22) and their value structures (
MethodCallArrayand the flags) are named only. - Reference: the layout of the data inside the
ImageListStreamerandAxHost.Statemembers is not described.
Unknown
- Reference: the MS-NRBF specification was not opened here; the record layouts are the ones the viewer’s reader implements and its tests check against streams built by hand and the files found on this Mac.