// dnlib: See LICENSE.txt for more info using System; using dnlib.IO; namespace dnlib.PE { /// /// Reads all PE sections from a PE stream, for more information see https://docs.microsoft.com/en-us/windows/win32/debug/pe-format /// sealed class PEInfo { readonly ImageDosHeader imageDosHeader; readonly ImageNTHeaders imageNTHeaders; readonly ImageSectionHeader[] imageSectionHeaders; /// /// Returns the DOS header /// public ImageDosHeader ImageDosHeader => imageDosHeader; /// /// Returns the NT headers /// public ImageNTHeaders ImageNTHeaders => imageNTHeaders; /// /// Returns the section headers /// public ImageSectionHeader[] ImageSectionHeaders => imageSectionHeaders; /// /// Constructor /// /// PE file reader pointing to the start of this section /// Verify sections /// Thrown if verification fails public PEInfo(ref DataReader reader, bool verify) { reader.Position = 0; imageDosHeader = new ImageDosHeader(ref reader, verify); if (verify && imageDosHeader.NTHeadersOffset == 0) throw new BadImageFormatException("Invalid NT headers offset"); reader.Position = imageDosHeader.NTHeadersOffset; imageNTHeaders = new ImageNTHeaders(ref reader, verify); reader.Position = (uint)imageNTHeaders.OptionalHeader.StartOffset + imageNTHeaders.FileHeader.SizeOfOptionalHeader; int numSections = imageNTHeaders.FileHeader.NumberOfSections; if (numSections > 0) { // Mono doesn't verify the section count var tempReader = reader; tempReader.Position += 0x14; uint firstSectionOffset = tempReader.ReadUInt32(); numSections = Math.Min(numSections, (int)((firstSectionOffset - reader.Position) / 0x28)); } imageSectionHeaders = new ImageSectionHeader[numSections]; for (int i = 0; i < imageSectionHeaders.Length; i++) imageSectionHeaders[i] = new ImageSectionHeader(ref reader, verify); } /// /// Returns the first that has data at file offset /// /// /// The file offset /// public ImageSectionHeader ToImageSectionHeader(FileOffset offset) { foreach (var section in imageSectionHeaders) { if ((uint)offset >= section.PointerToRawData && (uint)offset < section.PointerToRawData + section.SizeOfRawData) return section; } return null; } /// /// Returns the first that has data at RVA /// /// /// The RVA /// public ImageSectionHeader ToImageSectionHeader(RVA rva) { uint alignment = imageNTHeaders.OptionalHeader.SectionAlignment; foreach (var section in imageSectionHeaders) { if (rva >= section.VirtualAddress && rva < section.VirtualAddress + DotNet.Utils.AlignUp(section.VirtualSize, alignment)) return section; } return null; } /// /// Converts a to an , returns 0 if out of range /// /// The file offset to convert /// The RVA public RVA ToRVA(FileOffset offset) { // In pe headers if (imageSectionHeaders.Length == 0) return (RVA)offset; // In pe additional data, like digital signature, won't be loaded into memory var lastSection = imageSectionHeaders[imageSectionHeaders.Length - 1]; if ((uint)offset > lastSection.PointerToRawData + lastSection.SizeOfRawData) return 0; // In a section var section = ToImageSectionHeader(offset); if (section is not null) return (uint)(offset - section.PointerToRawData) + section.VirtualAddress; // In pe headers return (RVA)offset; } /// /// Converts an to a , returns 0 if out of range /// /// The RVA to convert /// The file offset public FileOffset ToFileOffset(RVA rva) { // Check if rva is larger than memory layout size if ((uint)rva >= imageNTHeaders.OptionalHeader.SizeOfImage) return 0; var section = ToImageSectionHeader(rva); if (section is not null) { uint offset = rva - section.VirtualAddress; // Virtual size may be bigger than raw size and there may be no corresponding file offset to rva if (offset < section.SizeOfRawData) return (FileOffset)offset + section.PointerToRawData; return 0; } // If not in any section, rva is in pe headers and don't convert it return (FileOffset)rva; } static ulong AlignUp(ulong val, uint alignment) => (val + alignment - 1) & ~(ulong)(alignment - 1); /// /// Returns size of image rounded up to /// /// It calculates the size itself, and does not return /// Size of image in bytes public uint GetImageSize() { var optHdr = ImageNTHeaders.OptionalHeader; uint alignment = optHdr.SectionAlignment; if (imageSectionHeaders.Length == 0) return (uint)AlignUp(optHdr.SizeOfHeaders, alignment); // Section headers must be in ascending order and adjacent var section = imageSectionHeaders[imageSectionHeaders.Length - 1]; return (uint)Math.Min(AlignUp((ulong)section.VirtualAddress + section.VirtualSize, alignment), uint.MaxValue); } } }