obfuz/Plugins/dnlib/PE/PEInfo.cs

154 lines
5.6 KiB
C#

// dnlib: See LICENSE.txt for more info
using System;
using dnlib.IO;
namespace dnlib.PE {
/// <summary>
/// Reads all PE sections from a PE stream, for more information see https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
/// </summary>
sealed class PEInfo {
readonly ImageDosHeader imageDosHeader;
readonly ImageNTHeaders imageNTHeaders;
readonly ImageSectionHeader[] imageSectionHeaders;
/// <summary>
/// Returns the DOS header
/// </summary>
public ImageDosHeader ImageDosHeader => imageDosHeader;
/// <summary>
/// Returns the NT headers
/// </summary>
public ImageNTHeaders ImageNTHeaders => imageNTHeaders;
/// <summary>
/// Returns the section headers
/// </summary>
public ImageSectionHeader[] ImageSectionHeaders => imageSectionHeaders;
/// <summary>
/// Constructor
/// </summary>
/// <param name="reader">PE file reader pointing to the start of this section</param>
/// <param name="verify">Verify sections</param>
/// <exception cref="BadImageFormatException">Thrown if verification fails</exception>
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);
}
/// <summary>
/// Returns the first <see cref="ImageSectionHeader"/> that has data at file offset
/// <paramref name="offset"/>
/// </summary>
/// <param name="offset">The file offset</param>
/// <returns></returns>
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;
}
/// <summary>
/// Returns the first <see cref="ImageSectionHeader"/> that has data at RVA
/// <paramref name="rva"/>
/// </summary>
/// <param name="rva">The RVA</param>
/// <returns></returns>
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;
}
/// <summary>
/// Converts a <see cref="FileOffset"/> to an <see cref="RVA"/>, returns 0 if out of range
/// </summary>
/// <param name="offset">The file offset to convert</param>
/// <returns>The RVA</returns>
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;
}
/// <summary>
/// Converts an <see cref="RVA"/> to a <see cref="FileOffset"/>, returns 0 if out of range
/// </summary>
/// <param name="rva">The RVA to convert</param>
/// <returns>The file offset</returns>
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);
/// <summary>
/// Returns size of image rounded up to <see cref="IImageOptionalHeader.SectionAlignment"/>
/// </summary>
/// <remarks>It calculates the size itself, and does not return <see cref="IImageOptionalHeader.SizeOfImage"/></remarks>
/// <returns>Size of image in bytes</returns>
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);
}
}
}