// 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();
}
}
}