Buy Me a Coffee

Buy Me a Coffee!

Saturday, January 21, 2017

Dissecting C# Executables: Part 12

Ok, let's get back to the metadata table. (Notice how I just blindly skip over the part I don't understand yet?)  From Part 10 we see in the CLR header the RVA for the Metadata:


00000210   70 20 00 00 EC 02 00 00  01 00 00 00 01 00 00 06   p ··ì···········


We have the word MetaData which is made up of the half-word MetaData RVA and MetaData Size which are 0x00002070 and 0x000002EC respectively.   So if we start with the VirtualAddress from Part 7 of 0x00002000 and then know that that is where the PointerToRawData of 0x00000200 will be loaded.  So subtracting the VirtualAddress and adding the result to the PointertoRawData we get to 0x00000270 as the start of the Metadata.  The Metadata section is going to be 0x2EC in size.  The Metadata Storage signature is as follows:


Type
Field
Description
DWORD
lSignature
“Magic” signature for physical metadata, currently 0x424A5342.
WORD
iMajorVer
Major version (1)
WORD
iMinorVer
Minor version (1)
DWORD
iExtraData
Reserved; set to 0
DWORD
iVersionString
Length of the version string
BYTE[]
pVersion
Version string

(Note: the Word in the chart is 16bits or what I call a Half-Word.  The Dword is 4 bytes, or what I call a Word)
Let't start with what we had in Part 11:



00000270   42 53 4A 42 01 00 01 00  00 00 00 00 0C 00 00 00   BSJB············

00000280   76 32 2E 30 2E 35 30 37  32 37 00 00 00 00 05 00   v2.0.50727······
We start with the lSignature or "Magic" signature string which stands for Brian Harry, Susan Radke-Sproull, Jason Zander, and Bill Evans.  The iMajorVer is 0x0001 and the iMinorVer is 0x0001.  the iExtraData is 0x00 and the iVersionString is 0x0000000C or 12.  The pVersion is thus 12 bytes long or 0x76322E302E35303732370000 or v2.0.50727.


After the Storage Signature we have the Storage Header which has the following structure:



Type
Field
Description
BYTE
fFlags
Reserved; set to 0
BYTE
[padding]
WORD
iStreams
Number of streams


The fFlags is set to 0x00 there is a padding byte which is also set to 0x00 and finally the iStreams which is 0x0005.  Moving on, we get to one of the 5 Managed Metadata Stream Headers which has the following format:




Type
Field
Description
DWORD
iOffset
Offset in the file for this stream.
DWORD
iSize
Size of the stream in bytes.
char[32 ]
rcName
Name of the stream; a zero-terminated ASCII string no longer than 31 characters (plus zero terminator). The name might be shorter, in which case the size of the stream header is correspondingly reduced, padded to the 4-byte boundary.


 
So, given our data:


00000290   6C 00 00 00 04 01 00 00  23 7E 00 00 70 01 00 00   l·······#~··p···
000002A0   00 01 00 00 23 53 74 72  69 6E 67 73 00 00 00 00   ····#Strings····
000002B0   70 02 00 00 1C 00 00 00  23 55 53 00 8C 02 00 00   p·······#US·?···
000002C0   10 00 00 00 23 47 55 49  44 00 00 00 9C 02 00 00   ····#GUID···?···
000002D0   50 00 00 00 23 42 6C 6F  62 00 00 00 00 00 00 00   P···#Blob·······
000002E0   02 00 00 01 47 15 00 00  09 00 00 00 00 FA 01 33   ····G········ú·3
000002F0   00 16 00 00 01 00 00 00  06 00 00 00 02 00 00 00   ················
00000300   02 00 00 00 01 00 00 00  06 00 00 00 02 00 00 00   ················
00000310   01 00 00 00 01 00 00 00  00 00 0A 00 01 00 00 00   ················
00000320   00 00 06 00 44 00 3D 00  06 00 76 00 56 00 06 00   ····D·=···v·V···
00000330   96 00 56 00 06 00 CC 00  3D 00 06 00 DE 00 3D 00   ?·V···I·=···_·=·


We see that the iOffset is 0x0000006C and the iSize is 0x00000104.  The rcName is a null terminated Ascii string padded to a 4 byte boundry so the value is #~ and there is one padding byte.  From the book, this is a compressed metadata stream which contains metadata tables.  It is 0x104 or 260 bytes in length.  It will start 0x6C or 108 bytes in.


The next header has an iOffset of 0x0000170 or 368 bytes and an iSize of 0x00000100 or 256 bytes.  The rcName is #Strings.  Note that it finishes the line because the null terminator for the string crosses into the next 4 byte section.


The next header has an iOffset of 0x00000270 or 624 bytes and an iSize of 0x0000001C or 28 bytes.  The rcName is #US.


The fourth header contains an iOffset of 0x0000028C or 652 bytes and an iSize of 0x00000010 or 16.  The rcName is #GUID.


The last, or fifth header has an iOffset of 0x000029C or 668 bytes and an iSize of 0x00000050 or 80 bytes.  The rcName is #Blob.  Note that the rest of the 4 byte section is padded out.


Now we get to the #~ stream because we started the section at 0x270 and we have an offset of  0x6C which means it starts at 0x2DC.  The Metadata Table comes in both the #~ (optimized) and #- (un-optomized) versions, but they share a common header:



Size
Field
Description
4 bytes
Reserved
Reserved; set to 0.
1 byte
Major
Major version of the table schema (1 for v1.0 and v1.1; 2 for v2.0 or later).
1 byte
Minor
Minor version of the table schema (0 for all versions).
1 byte
Heaps
Binary flags indicate the offset sizes to be used within the heaps
.•        4-byte unsigned integer offset is indicated by 0x01 for a string heap, 0x02 for a GUID heap, and 0x04 for a blob heap
.•        If a flag is not set, the respective heap offset is a 2-byte unsigned integer
.•        A #- stream can also have special flags set: flag 0x20, indicating that the stream contains only changes made during an edit-and-continue session, and flag 0x80, indicating that the metadata might contain items marked as deleted.
1 byte
Rid
Bit width of the maximal record index to all tables of the metadata; calculated at run time (during the metadata stream initialization).
8 bytes
MaskValid
Bit vector of present tables, each bit representing one table (1 if present).
8 bytes
Sorted
Bit vector of sorted tables, each bit representing a respective table (1 if sorted).


 Let's dig into that starting next time.  Until then, keep your code clean!