// dnlib: See LICENSE.txt for more info using System; using System.Threading; using dnlib.Utils; using dnlib.IO; using dnlib.PE; using dnlib.Threading; namespace dnlib.W32Resources { /// /// Win32 resources base class /// public abstract class Win32Resources : IDisposable { /// /// Gets/sets the root directory /// public abstract ResourceDirectory Root { get; set; } /// /// Finds a /// /// Type /// The or null if none found public ResourceDirectory Find(ResourceName type) { var dir = Root; if (dir is null) return null; return dir.FindDirectory(type); } /// /// Finds a /// /// Type /// Name /// The or null if none found public ResourceDirectory Find(ResourceName type, ResourceName name) { var dir = Find(type); if (dir is null) return null; return dir.FindDirectory(name); } /// /// Finds a /// /// Type /// Name /// Language ID /// The or null if none found public ResourceData Find(ResourceName type, ResourceName name, ResourceName langId) { var dir = Find(type, name); if (dir is null) return null; return dir.FindData(langId); } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Dispose method /// /// true if called by protected virtual void Dispose(bool disposing) { if (!disposing) return; Root = null; // Property handler will call Dispose() } } /// /// Win32 resources class created by the user /// public class Win32ResourcesUser : Win32Resources { ResourceDirectory root = new ResourceDirectoryUser(new ResourceName("root")); /// public override ResourceDirectory Root { get => root; set => Interlocked.Exchange(ref root, value); } } /// /// Win32 resources class created from a PE file /// public sealed class Win32ResourcesPE : Win32Resources { /// /// Converts data RVAs to file offsets in /// readonly IRvaFileOffsetConverter rvaConverter; /// /// This reader only reads the raw data. The data RVA is found in the data header and /// it's first converted to a file offset using . This file /// offset is where we'll read from using this reader. /// DataReaderFactory dataReader_factory; uint dataReader_offset; uint dataReader_length; bool owns_dataReader_factory; /// /// This reader only reads the directory entries and data headers. The data is read /// by /// DataReaderFactory rsrcReader_factory; uint rsrcReader_offset; uint rsrcReader_length; bool owns_rsrcReader_factory; UserValue root; #if THREAD_SAFE readonly Lock theLock = Lock.Create(); #endif /// public override ResourceDirectory Root { get => root.Value; set { if (root.IsValueInitialized) { var origValue = root.Value; if (origValue == value) return; } root.Value = value; } } /// /// Gets the resource reader /// internal DataReader GetResourceReader() => rsrcReader_factory.CreateReader(rsrcReader_offset, rsrcReader_length); /// /// Constructor /// /// / converter /// Reader for the whole Win32 resources section (usually /// the .rsrc section). It's used to read 's and /// 's but not the actual data blob. /// Offset of resource section /// Length of resource section /// true if this instance can dispose of /// Data reader (it's used after converting an /// to a ) /// Offset of resource section /// Length of resource section /// true if this instance can dispose of public Win32ResourcesPE(IRvaFileOffsetConverter rvaConverter, DataReaderFactory rsrcReader_factory, uint rsrcReader_offset, uint rsrcReader_length, bool owns_rsrcReader_factory, DataReaderFactory dataReader_factory, uint dataReader_offset, uint dataReader_length, bool owns_dataReader_factory) { this.rvaConverter = rvaConverter ?? throw new ArgumentNullException(nameof(rvaConverter)); this.rsrcReader_factory = rsrcReader_factory ?? throw new ArgumentNullException(nameof(rsrcReader_factory)); this.rsrcReader_offset = rsrcReader_offset; this.rsrcReader_length = rsrcReader_length; this.owns_rsrcReader_factory = owns_rsrcReader_factory; this.dataReader_factory = dataReader_factory ?? throw new ArgumentNullException(nameof(dataReader_factory)); this.dataReader_offset = dataReader_offset; this.dataReader_length = dataReader_length; this.owns_dataReader_factory = owns_dataReader_factory; Initialize(); } /// /// Constructor /// /// The PE image public Win32ResourcesPE(IPEImage peImage) : this(peImage, null, 0, 0, false) { } /// /// Constructor /// /// The PE image /// Reader for the whole Win32 resources section (usually /// the .rsrc section) or null if we should create one from the resource data /// directory in the optional header /// Offset of resource section /// Length of resource section /// true if this instance can dispose of public Win32ResourcesPE(IPEImage peImage, DataReaderFactory rsrcReader_factory, uint rsrcReader_offset, uint rsrcReader_length, bool owns_rsrcReader_factory) { rvaConverter = peImage ?? throw new ArgumentNullException(nameof(peImage)); dataReader_factory = peImage.DataReaderFactory; dataReader_offset = 0; dataReader_length = dataReader_factory.Length; if (rsrcReader_factory is not null) { this.rsrcReader_factory = rsrcReader_factory; this.rsrcReader_offset = rsrcReader_offset; this.rsrcReader_length = rsrcReader_length; this.owns_rsrcReader_factory = owns_rsrcReader_factory; } else { var dataDir = peImage.ImageNTHeaders.OptionalHeader.DataDirectories[2]; if (dataDir.VirtualAddress != 0 && dataDir.Size != 0) { var reader = peImage.CreateReader(dataDir.VirtualAddress, dataDir.Size); this.rsrcReader_factory = peImage.DataReaderFactory; this.rsrcReader_offset = reader.StartOffset; this.rsrcReader_length = reader.Length; } else { this.rsrcReader_factory = ByteArrayDataReaderFactory.Create(Array2.Empty(), filename: null); this.rsrcReader_offset = 0; this.rsrcReader_length = 0; } } Initialize(); } void Initialize() { root.ReadOriginalValue = () => { var rsrcReader_factory = this.rsrcReader_factory; if (rsrcReader_factory is null) return null; // It's disposed var reader = rsrcReader_factory.CreateReader(rsrcReader_offset, rsrcReader_length); return new ResourceDirectoryPE(0, new ResourceName("root"), this, ref reader); }; #if THREAD_SAFE root.Lock = theLock; #endif } /// /// Creates a new data reader /// /// RVA of data /// Size of data /// public DataReader CreateReader(RVA rva, uint size) { GetDataReaderInfo(rva, size, out var dataReaderFactory, out uint dataOffset, out uint dataLength); return dataReaderFactory.CreateReader(dataOffset, dataLength); } internal void GetDataReaderInfo(RVA rva, uint size, out DataReaderFactory dataReaderFactory, out uint dataOffset, out uint dataLength) { dataOffset = (uint)rvaConverter.ToFileOffset(rva); if ((ulong)dataOffset + size <= dataReader_factory.Length) { dataReaderFactory = dataReader_factory; dataLength = size; return; } else { dataReaderFactory = ByteArrayDataReaderFactory.Create(Array2.Empty(), filename: null); dataOffset = 0; dataLength = 0; } } /// protected override void Dispose(bool disposing) { if (!disposing) return; if (owns_dataReader_factory) dataReader_factory?.Dispose(); if (owns_rsrcReader_factory) rsrcReader_factory?.Dispose(); dataReader_factory = null; rsrcReader_factory = null; base.Dispose(disposing); } } }