// dnlib: See LICENSE.txt for more info using System; using System.Collections.Generic; using System.IO; using dnlib.IO; using dnlib.Utils; using dnlib.W32Resources; using dnlib.Threading; namespace dnlib.PE { /// /// Image layout /// public enum ImageLayout { /// /// Use this if the PE file has a normal structure (eg. it's been read from a file on disk) /// File, /// /// Use this if the PE file has been loaded into memory by the OS PE file loader /// Memory, } /// /// Accesses a PE file /// public sealed class PEImage : IInternalPEImage { // Default to false because an OS loaded PE image may contain memory holes. If there // are memory holes, other code (eg. .NET resource creator) must verify that all memory // is available, which will be slower. const bool USE_MEMORY_LAYOUT_WITH_MAPPED_FILES = false; static readonly IPEType MemoryLayout = new MemoryPEType(); static readonly IPEType FileLayout = new FilePEType(); DataReaderFactory dataReaderFactory; IPEType peType; PEInfo peInfo; UserValue win32Resources; #if THREAD_SAFE readonly Lock theLock = Lock.Create(); #endif sealed class FilePEType : IPEType { public RVA ToRVA(PEInfo peInfo, FileOffset offset) => peInfo.ToRVA(offset); public FileOffset ToFileOffset(PEInfo peInfo, RVA rva) => peInfo.ToFileOffset(rva); } sealed class MemoryPEType : IPEType { public RVA ToRVA(PEInfo peInfo, FileOffset offset) => (RVA)offset; public FileOffset ToFileOffset(PEInfo peInfo, RVA rva) => (FileOffset)rva; } /// public bool IsFileImageLayout => peType is FilePEType; /// public bool MayHaveInvalidAddresses => !IsFileImageLayout; /// public string Filename => dataReaderFactory.Filename; /// public ImageDosHeader ImageDosHeader => peInfo.ImageDosHeader; /// public ImageNTHeaders ImageNTHeaders => peInfo.ImageNTHeaders; /// public IList ImageSectionHeaders => peInfo.ImageSectionHeaders; /// public IList ImageDebugDirectories { get { if (imageDebugDirectories is null) imageDebugDirectories = ReadImageDebugDirectories(); return imageDebugDirectories; } } ImageDebugDirectory[] imageDebugDirectories; /// public DataReaderFactory DataReaderFactory => dataReaderFactory; /// public Win32Resources Win32Resources { get => win32Resources.Value; set { IDisposable origValue = null; if (win32Resources.IsValueInitialized) { origValue = win32Resources.Value; if (origValue == value) return; } win32Resources.Value = value; if (origValue is not null) origValue.Dispose(); } } /// /// Constructor /// /// Data reader factory /// Image layout /// Verify PE file data public PEImage(DataReaderFactory dataReaderFactory, ImageLayout imageLayout, bool verify) { try { this.dataReaderFactory = dataReaderFactory; peType = ConvertImageLayout(imageLayout); var reader = dataReaderFactory.CreateReader(); peInfo = new PEInfo(ref reader, verify); Initialize(); } catch { Dispose(); throw; } } void Initialize() { win32Resources.ReadOriginalValue = () => { var dataDir = peInfo.ImageNTHeaders.OptionalHeader.DataDirectories[2]; if (dataDir.VirtualAddress == 0 || dataDir.Size == 0) return null; return new Win32ResourcesPE(this); }; #if THREAD_SAFE win32Resources.Lock = theLock; #endif } static IPEType ConvertImageLayout(ImageLayout imageLayout) => imageLayout switch { ImageLayout.File => FileLayout, ImageLayout.Memory => MemoryLayout, _ => throw new ArgumentException("imageLayout"), }; /// /// Constructor /// /// Name of the file /// true if we should map it as an executable /// Verify PE file data internal PEImage(string filename, bool mapAsImage, bool verify) : this(DataReaderFactoryFactory.Create(filename, mapAsImage), mapAsImage ? ImageLayout.Memory : ImageLayout.File, verify) { try { if (mapAsImage && dataReaderFactory is MemoryMappedDataReaderFactory) ((MemoryMappedDataReaderFactory)dataReaderFactory).SetLength(peInfo.GetImageSize()); } catch { Dispose(); throw; } } /// /// Constructor /// /// Name of the file /// Verify PE file data public PEImage(string filename, bool verify) : this(filename, USE_MEMORY_LAYOUT_WITH_MAPPED_FILES, verify) { } /// /// Constructor /// /// Name of the file public PEImage(string filename) : this(filename, true) { } /// /// Constructor /// /// The PE file data /// Filename or null /// Image layout /// Verify PE file data public PEImage(byte[] data, string filename, ImageLayout imageLayout, bool verify) : this(ByteArrayDataReaderFactory.Create(data, filename), imageLayout, verify) { } /// /// Constructor /// /// The PE file data /// Image layout /// Verify PE file data public PEImage(byte[] data, ImageLayout imageLayout, bool verify) : this(data, null, imageLayout, verify) { } /// /// Constructor /// /// The PE file data /// Verify PE file data public PEImage(byte[] data, bool verify) : this(data, null, ImageLayout.File, verify) { } /// /// Constructor /// /// The PE file data /// Filename or null /// Verify PE file data public PEImage(byte[] data, string filename, bool verify) : this(data, filename, ImageLayout.File, verify) { } /// /// Constructor /// /// The PE file data public PEImage(byte[] data) : this(data, null, true) { } /// /// Constructor /// /// The PE file data /// Filename or null public PEImage(byte[] data, string filename) : this(data, filename, true) { } /// /// Constructor /// /// Address of PE image /// Length of PE image /// Image layout /// Verify PE file data public unsafe PEImage(IntPtr baseAddr, uint length, ImageLayout imageLayout, bool verify) : this(NativeMemoryDataReaderFactory.Create((byte*)baseAddr, length, filename: null), imageLayout, verify) { } /// /// Constructor /// /// Address of PE image /// Length of PE image /// Verify PE file data public PEImage(IntPtr baseAddr, uint length, bool verify) : this(baseAddr, length, ImageLayout.Memory, verify) { } /// /// Constructor /// /// Address of PE image /// Length of PE image public PEImage(IntPtr baseAddr, uint length) : this(baseAddr, length, true) { } /// /// Constructor /// /// Address of PE image /// Image layout /// Verify PE file data public unsafe PEImage(IntPtr baseAddr, ImageLayout imageLayout, bool verify) : this(NativeMemoryDataReaderFactory.Create((byte*)baseAddr, 0x10000, filename: null), imageLayout, verify) { try { ((NativeMemoryDataReaderFactory)dataReaderFactory).SetLength(peInfo.GetImageSize()); } catch { Dispose(); throw; } } /// /// Constructor /// /// Address of PE image /// Verify PE file data public PEImage(IntPtr baseAddr, bool verify) : this(baseAddr, ImageLayout.Memory, verify) { } /// /// Constructor /// /// Address of PE image public PEImage(IntPtr baseAddr) : this(baseAddr, true) { } /// public RVA ToRVA(FileOffset offset) => peType.ToRVA(peInfo, offset); /// public FileOffset ToFileOffset(RVA rva) => peType.ToFileOffset(peInfo, rva); /// public void Dispose() { IDisposable id; if (win32Resources.IsValueInitialized && (id = win32Resources.Value) is not null) id.Dispose(); dataReaderFactory?.Dispose(); win32Resources.Value = null; dataReaderFactory = null; peType = null; peInfo = null; } /// public DataReader CreateReader(FileOffset offset) => DataReaderFactory.CreateReader((uint)offset, DataReaderFactory.Length - (uint)offset); /// public DataReader CreateReader(FileOffset offset, uint length) => DataReaderFactory.CreateReader((uint)offset, length); /// public DataReader CreateReader(RVA rva) => CreateReader(ToFileOffset(rva)); /// public DataReader CreateReader(RVA rva, uint length) => CreateReader(ToFileOffset(rva), length); /// public DataReader CreateReader() => DataReaderFactory.CreateReader(); void IInternalPEImage.UnsafeDisableMemoryMappedIO() { if (dataReaderFactory is MemoryMappedDataReaderFactory creator) creator.UnsafeDisableMemoryMappedIO(); } bool IInternalPEImage.IsMemoryMappedIO { get { var creator = dataReaderFactory as MemoryMappedDataReaderFactory; return creator is null ? false : creator.IsMemoryMappedIO; } } ImageDebugDirectory[] ReadImageDebugDirectories() { try { var dataDir = ImageNTHeaders.OptionalHeader.DataDirectories[6]; if (dataDir.VirtualAddress == 0) return Array2.Empty(); var reader = DataReaderFactory.CreateReader(); if (dataDir.Size > reader.Length) return Array2.Empty(); int count = (int)(dataDir.Size / 0x1C); if (count == 0) return Array2.Empty(); reader.CurrentOffset = (uint)ToFileOffset(dataDir.VirtualAddress); if ((ulong)reader.CurrentOffset + dataDir.Size > reader.Length) return Array2.Empty(); var res = new ImageDebugDirectory[count]; for (int i = 0; i < res.Length; i++) res[i] = new ImageDebugDirectory(ref reader, true); return res; } catch (IOException) { } return Array2.Empty(); } } }