internal static AssemblyMetadata ReadAssemblyMetadata(Stream stream, AssemblyMetadataFields fields) { long length = stream.Length; if (length < 0x40) { return(null); } BinaryReader reader = new BinaryReader(stream); // Read the pointer to the PE header. stream.Position = 0x3c; uint peHeaderPtr = reader.ReadUInt32(); if (peHeaderPtr == 0) { peHeaderPtr = 0x80; } // Ensure there is at least enough room for the following structures: // 24 byte PE Signature & Header // 28 byte Standard Fields (24 bytes for PE32+) // 68 byte NT Fields (88 bytes for PE32+) // >= 128 byte Data Dictionary Table if (peHeaderPtr > length - 256) { return(null); } // Check the PE signature. Should equal 'PE\0\0'. stream.Position = peHeaderPtr; uint peSignature = reader.ReadUInt32(); if (peSignature != 0x00004550) { return(null); } // Read PE header fields. ushort machine = reader.ReadUInt16(); ushort numberOfSections = reader.ReadUInt16(); uint timeStamp = reader.ReadUInt32(); uint symbolTablePtr = reader.ReadUInt32(); uint numberOfSymbols = reader.ReadUInt32(); ushort optionalHeaderSize = reader.ReadUInt16(); ushort characteristics = reader.ReadUInt16(); // Read PE magic number from Standard Fields to determine format. PEFormat peFormat = (PEFormat)reader.ReadUInt16(); if (peFormat != PEFormat.PE32 && peFormat != PEFormat.PE32Plus) { return(null); } // Read the 15th Data Dictionary RVA field which contains the CLI header RVA. // When this is non-zero then the file contains CLI data otherwise not. stream.Position = peHeaderPtr + (peFormat == PEFormat.PE32 ? 232 : 248); uint cliHeaderRva = reader.ReadUInt32(); if (cliHeaderRva == 0) { return(null); } // Read section headers. Each one is 40 bytes. // 8 byte Name // 4 byte Virtual Size // 4 byte Virtual Address // 4 byte Data Size // 4 byte Data Pointer // ... total of 40 bytes uint sectionTablePtr = peHeaderPtr + 24 + optionalHeaderSize; Section[] sections = new Section[numberOfSections]; for (int i = 0; i < numberOfSections; i++) { stream.Position = sectionTablePtr + i * 40 + 8; Section section = new Section(); section.VirtualSize = reader.ReadUInt32(); section.VirtualAddress = reader.ReadUInt32(); reader.ReadUInt32(); section.Pointer = reader.ReadUInt32(); sections[i] = section; } // Read parts of the CLI header. uint cliHeaderPtr = ResolveRva(sections, cliHeaderRva); if (cliHeaderPtr == 0) { return(null); } stream.Position = cliHeaderPtr + 4; ushort majorRuntimeVersion = reader.ReadUInt16(); ushort minorRuntimeVersion = reader.ReadUInt16(); uint metadataRva = reader.ReadUInt32(); uint metadataSize = reader.ReadUInt32(); CorFlags corflags = (CorFlags)reader.ReadUInt32(); // Read optional fields. AssemblyName assemblyName = null; IList <AssemblyName> assemblyReferences = null; string runtimeVersion = null; if ((fields & AssemblyMetadataFields.RuntimeVersion) != 0) { uint metadataPtr = ResolveRva(sections, metadataRva); stream.Position = metadataPtr + 12; int paddedRuntimeVersionLength = reader.ReadInt32(); byte[] runtimeVersionBytes = reader.ReadBytes(paddedRuntimeVersionLength); int runtimeVersionLength = 0; while (runtimeVersionLength < paddedRuntimeVersionLength && runtimeVersionBytes[runtimeVersionLength] != 0) { runtimeVersionLength += 1; } runtimeVersion = Encoding.UTF8.GetString(runtimeVersionBytes, 0, runtimeVersionLength); } if ((fields & (AssemblyMetadataFields.AssemblyName | AssemblyMetadataFields.AssemblyReferences)) != 0) { // Using Cecil. stream.Position = 0; var imageReader = new ImageReader(stream); if ((fields & AssemblyMetadataFields.AssemblyName) != 0) { assemblyName = imageReader.GetAssemblyName(); } if ((fields & AssemblyMetadataFields.AssemblyReferences) != 0) { assemblyReferences = imageReader.GetAssemblyReferences(); } } // Done. return(new AssemblyMetadata(majorRuntimeVersion, minorRuntimeVersion, corflags, peFormat, assemblyName, assemblyReferences, runtimeVersion)); }