File: Interop\PortableExecutable\PortableExecutableHeader.cs
Web Access
Project: src\src\SignCheck\Microsoft.SignCheck\Microsoft.DotNet.SignCheckLibrary.csproj (Microsoft.DotNet.SignCheckLibrary)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Linq;
using System.IO;
using System.Collections.Generic;
using System.Runtime.InteropServices;
 
namespace Microsoft.SignCheck.Interop.PortableExecutable
{
    public class PortableExecutableHeader
    {
        private IMAGE_COR20_HEADER _imageCor20Header;
        private IMAGE_DOS_HEADER _imageDosHeader;
        private IMAGE_NT_HEADERS32 _imageNTHeaders32;
        private IMAGE_NT_HEADERS64 _imageNTHeaders64;
        private List<IMAGE_SECTION_HEADER> _imageSectionHeaders;
 
        /// <summary>
        /// The <see cref="IMAGE_DATA_DIRECTORY"/> entry pointing to the CLR header.
        /// </summary>
        public IMAGE_DATA_DIRECTORY CLRRuntimeHeader
        {
            get
            {
                if (OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR32_MAGIC)
                {
                    return ImageNTHeaders32.OptionalHeader32.CLRRuntimeHeader;
                }
                else if (OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR64_MAGIC)
                {
                    return ImageNTHeaders64.OptionalHeader64.CLRRuntimeHeader;
                }
                else
                {
                    return ImageDataDirectory.Empty;
                }
            }
        }
 
        public IMAGE_FILE_HEADER FileHeader
        {
            get
            {
                if (IsPE32)
                {
                    return ImageNTHeaders32.FileHeader;
                }
                else if (IsPE64)
                {
                    return ImageNTHeaders64.FileHeader;
                }
 
                return new IMAGE_FILE_HEADER();
            }
        }
 
        /// <summary>
        /// The offset of the first <see cref="IMAGE_SECTION_HEADER"/>.
        /// </summary>
        public uint FirstImageSectionHeaderOffset
        {
            get;
            private set;
        }
 
        /// <summary>
        /// Exposes access to the CLR runtime header.
        /// </summary>
        public IMAGE_COR20_HEADER ImageCor20Header
        {
            get
            {
                return _imageCor20Header;
            }
        }
 
        /// <summary>
        /// Exposes access to the <see cref="IMAGE_DOS_HEADER"/> structure of the PE binary.
        /// </summary>
        public IMAGE_DOS_HEADER ImageDosHeader
        {
            get
            {
                return _imageDosHeader;
            }
 
            private set
            {
                _imageDosHeader = value;
            }
        }
 
        /// <summary>
        /// Exposes access to the <see cref="IMAGE_NT_HEADERS32"/> structure of the PE binary.
        /// </summary>
        public IMAGE_NT_HEADERS32 ImageNTHeaders32
        {
            get
            {
                return _imageNTHeaders32;
            }
 
            private set
            {
                _imageNTHeaders32 = value;
            }
        }
 
        /// <summary>
        /// Exposes access to the <see cref="IMAGE_NT_HEADERS64"/> structure in the PE binary.
        /// </summary>
        public IMAGE_NT_HEADERS64 ImageNTHeaders64
        {
            get
            {
                return _imageNTHeaders64;
            }
 
            private set
            {
                _imageNTHeaders64 = value;
            }
        }
 
        /// <summary>
        /// The offset of the IMAGE_NT_HEADERS structure. The structure can either be an <see cref="IMAGE_NT_HEADERS32"/> or
        /// <see cref="IMAGE_NT_HEADERS64"/>.
        /// </summary>
        public uint ImageNTHeadersOffset
        {
            get
            {
                return ImageDosHeader.e_lfanew;
            }
        }
 
        /// <summary>
        /// Determine if the IMAGE_NT_HEADERS Signature field contains the "PE00" signature.
        /// </summary>
        public bool IsValidNTHeader
        {
            get
            {
                return ImageNTHeadersSignature == ImageNTHeaders.IMAGE_NT_SIGNATURE;
            }
        }
 
        /// <summary>
        /// The signature of the IMAGE_NT_HEADERS structure.
        /// </summary>
        public uint ImageNTHeadersSignature
        {
            get
            {
                if (OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR32_MAGIC)
                {
                    return ImageNTHeaders32.Signature;
                }
                else if (OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR64_MAGIC)
                {
                    return ImageNTHeaders64.Signature;
                }
                else
                {
                    return 0;
                }
            }
        }
 
        /// <summary>
        /// An enumerable collection of <see cref="IMAGE_SECTION_HEADER"/>
        /// </summary>
        public IEnumerable<IMAGE_SECTION_HEADER> ImageSectionHeaders
        {
            get
            {
                if (_imageSectionHeaders == null)
                {
                    _imageSectionHeaders = new List<IMAGE_SECTION_HEADER>();
                }
                return _imageSectionHeaders;
            }
        }
 
        public bool IsILImage
        {
            get
            {
                return (ImageCor20Header.ManagedNativeHeader.Size == 0) && (ImageCor20Header.ManagedNativeHeader.VirtualAddress == 0);
            }
        }
 
        public bool IsManagedCode
        {
            get
            {
                return CLRRuntimeHeader.Size > 0;
            }
        }
 
        public bool IsPE32
        {
            get
            {
                return OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR32_MAGIC;
            }
        }
 
        public bool IsPE64
        {
            get
            {
                return OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR64_MAGIC;
            }
        }
 
        /// <summary>
        /// The number of <see cref="IMAGE_SECTION_HEADER"/> entries.
        /// </summary>
        public ushort NumberOfImageSectionHeaders
        {
            get;
            private set;
        }
 
        public ImageOptionalHeaderMagic OptionalHeaderMagic
        {
            get;
            private set;
        }
 
        /// <summary>
        /// The path of the executable image.
        /// </summary>
        public string Path
        {
            get;
            private set;
        }
 
        public uint SectionAlignment
        {
            get
            {
                if (OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR32_MAGIC)
                {
                    return ImageNTHeaders32.OptionalHeader32.SectionAlignment;
                }
                else if (OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR64_MAGIC)
                {
                    return ImageNTHeaders64.OptionalHeader64.SectionAlignment;
                }
 
                return 0;
            }
        }
 
        /// <summary>
        /// Initializes a new instance of the <see cref="PortableExecutableHeader"/> class using the specified path of a portable executable image."/>
        /// </summary>
        /// <param name="path">The path of the executable image.</param>
        public PortableExecutableHeader(string path)
        {
            Path = path;
            ImageDosHeader = IMAGE_DOS_HEADER.Read(path);
            ReadImageNTHeaders();
 
            if (IsValidNTHeader)
            {
                ReadImageSectionHeaders();
 
                if (CLRRuntimeHeader.VirtualAddress != 0)
                {
                    IMAGE_SECTION_HEADER section = GetSectionFromRva(CLRRuntimeHeader.VirtualAddress);
 
                    uint clrOffset = CLRRuntimeHeader.VirtualAddress - section.VirtualAddress + section.PointerToRawData;
                    _imageCor20Header = IMAGE_COR20_HEADER.Read(Path, clrOffset);
                }
            }
        }
 
        /// <summary>
        /// Locate the <see cref="IMAGE_SECTION_HEADER"/> that corresponds to the given RVA
        /// </summary>
        /// <param name="rva">The address to map to a header.</param>
        /// <returns></returns>
        public IMAGE_SECTION_HEADER GetSectionFromRva(uint rva)
        {
            IMAGE_SECTION_HEADER section = new IMAGE_SECTION_HEADER();
 
            var sections = ImageSectionHeaders.ToArray();
 
            int i = 0;
            while (i < FileHeader.NumberOfSections)
            {
                if (rva < sections[i].VirtualAddress + AlignTo(sections[i].VirtualSize, SectionAlignment))
                {
                    if (rva < sections[i].VirtualAddress)
                    {
                        return section;
                    }
                    else
                    {
                        return sections[i];
                    }
                }
                i++;
            }
            return section;
        }
 
        private void ReadImageNTHeaders()
        {
            // Calculate the offset where the IMAGE_OPTIONAL_HEADER starts
            UInt32 imageOptionalHeaderOffset = ImageNTHeadersOffset + 4 + (UInt32)Marshal.SizeOf<IMAGE_FILE_HEADER>();
 
            using (FileStream stream = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (BinaryReader reader = new BinaryReader(stream))
            {
                reader.BaseStream.Seek(imageOptionalHeaderOffset, SeekOrigin.Begin);
 
                // Retrieve the Magic field and then read the appropriate (32-bit or 64-bit) IMAGE_NT_OPTIONAL_HEADER
                OptionalHeaderMagic = (ImageOptionalHeaderMagic)reader.ReadUInt16();
 
                if (OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR32_MAGIC)
                {
                    ImageNTHeaders32 = IMAGE_NT_HEADERS32.Read(Path, ImageDosHeader.e_lfanew);
                    FirstImageSectionHeaderOffset = ImageNTHeadersOffset + (UInt32)Marshal.SizeOf<IMAGE_NT_HEADERS32>();
                    NumberOfImageSectionHeaders = ImageNTHeaders32.FileHeader.NumberOfSections;
                }
                else if (OptionalHeaderMagic == ImageOptionalHeaderMagic.IMAGE_NT_OPTIONAL_HDR64_MAGIC)
                {
                    ImageNTHeaders64 = IMAGE_NT_HEADERS64.Read(Path, ImageDosHeader.e_lfanew);
                    FirstImageSectionHeaderOffset = ImageNTHeadersOffset + (UInt32)Marshal.SizeOf<IMAGE_NT_HEADERS64>();
                    NumberOfImageSectionHeaders = ImageNTHeaders64.FileHeader.NumberOfSections;
                }
            }
        }
 
        private void ReadImageSectionHeaders()
        {
            using (FileStream stream = new FileStream(Path, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (BinaryReader reader = new BinaryReader(stream))
            {
                _imageSectionHeaders = IMAGE_SECTION_HEADER.Read(reader, NumberOfImageSectionHeaders, FirstImageSectionHeaderOffset);
            }
        }
 
        private static uint AlignTo(uint address, uint boundary)
        {
            return address + boundary - (address % boundary);
        }
    }
}