obfuz/Plugins/dnlib/W32Resources/Win32Resources.cs

267 lines
9.4 KiB
C#

// 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 {
/// <summary>
/// Win32 resources base class
/// </summary>
public abstract class Win32Resources : IDisposable {
/// <summary>
/// Gets/sets the root directory
/// </summary>
public abstract ResourceDirectory Root { get; set; }
/// <summary>
/// Finds a <see cref="ResourceDirectory"/>
/// </summary>
/// <param name="type">Type</param>
/// <returns>The <see cref="ResourceDirectory"/> or <c>null</c> if none found</returns>
public ResourceDirectory Find(ResourceName type) {
var dir = Root;
if (dir is null)
return null;
return dir.FindDirectory(type);
}
/// <summary>
/// Finds a <see cref="ResourceDirectory"/>
/// </summary>
/// <param name="type">Type</param>
/// <param name="name">Name</param>
/// <returns>The <see cref="ResourceDirectory"/> or <c>null</c> if none found</returns>
public ResourceDirectory Find(ResourceName type, ResourceName name) {
var dir = Find(type);
if (dir is null)
return null;
return dir.FindDirectory(name);
}
/// <summary>
/// Finds a <see cref="ResourceData"/>
/// </summary>
/// <param name="type">Type</param>
/// <param name="name">Name</param>
/// <param name="langId">Language ID</param>
/// <returns>The <see cref="ResourceData"/> or <c>null</c> if none found</returns>
public ResourceData Find(ResourceName type, ResourceName name, ResourceName langId) {
var dir = Find(type, name);
if (dir is null)
return null;
return dir.FindData(langId);
}
/// <inheritdoc/>
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Dispose method
/// </summary>
/// <param name="disposing"><c>true</c> if called by <see cref="Dispose()"/></param>
protected virtual void Dispose(bool disposing) {
if (!disposing)
return;
Root = null; // Property handler will call Dispose()
}
}
/// <summary>
/// Win32 resources class created by the user
/// </summary>
public class Win32ResourcesUser : Win32Resources {
ResourceDirectory root = new ResourceDirectoryUser(new ResourceName("root"));
/// <inheritdoc/>
public override ResourceDirectory Root {
get => root;
set => Interlocked.Exchange(ref root, value);
}
}
/// <summary>
/// Win32 resources class created from a PE file
/// </summary>
public sealed class Win32ResourcesPE : Win32Resources {
/// <summary>
/// Converts data RVAs to file offsets in <see cref="dataReader_factory"/>
/// </summary>
readonly IRvaFileOffsetConverter rvaConverter;
/// <summary>
/// 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 <see cref="rvaConverter"/>. This file
/// offset is where we'll read from using this reader.
/// </summary>
DataReaderFactory dataReader_factory;
uint dataReader_offset;
uint dataReader_length;
bool owns_dataReader_factory;
/// <summary>
/// This reader only reads the directory entries and data headers. The data is read
/// by <see cref="dataReader_factory"/>
/// </summary>
DataReaderFactory rsrcReader_factory;
uint rsrcReader_offset;
uint rsrcReader_length;
bool owns_rsrcReader_factory;
UserValue<ResourceDirectory> root;
#if THREAD_SAFE
readonly Lock theLock = Lock.Create();
#endif
/// <inheritdoc/>
public override ResourceDirectory Root {
get => root.Value;
set {
if (root.IsValueInitialized) {
var origValue = root.Value;
if (origValue == value)
return;
}
root.Value = value;
}
}
/// <summary>
/// Gets the resource reader
/// </summary>
internal DataReader GetResourceReader() => rsrcReader_factory.CreateReader(rsrcReader_offset, rsrcReader_length);
/// <summary>
/// Constructor
/// </summary>
/// <param name="rvaConverter"><see cref="RVA"/>/<see cref="FileOffset"/> converter</param>
/// <param name="rsrcReader_factory">Reader for the whole Win32 resources section (usually
/// the .rsrc section). It's used to read <see cref="ResourceDirectory"/>'s and
/// <see cref="ResourceData"/>'s but not the actual data blob.</param>
/// <param name="rsrcReader_offset">Offset of resource section</param>
/// <param name="rsrcReader_length">Length of resource section</param>
/// <param name="owns_rsrcReader_factory">true if this instance can dispose of <paramref name="rsrcReader_factory"/></param>
/// <param name="dataReader_factory">Data reader (it's used after converting an <see cref="RVA"/>
/// to a <see cref="FileOffset"/>)</param>
/// <param name="dataReader_offset">Offset of resource section</param>
/// <param name="dataReader_length">Length of resource section</param>
/// <param name="owns_dataReader_factory">true if this instance can dispose of <paramref name="dataReader_factory"/></param>
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();
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="peImage">The PE image</param>
public Win32ResourcesPE(IPEImage peImage)
: this(peImage, null, 0, 0, false) {
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="peImage">The PE image</param>
/// <param name="rsrcReader_factory">Reader for the whole Win32 resources section (usually
/// the .rsrc section) or <c>null</c> if we should create one from the resource data
/// directory in the optional header</param>
/// <param name="rsrcReader_offset">Offset of resource section</param>
/// <param name="rsrcReader_length">Length of resource section</param>
/// <param name="owns_rsrcReader_factory">true if this instance can dispose of <paramref name="rsrcReader_factory"/></param>
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<byte>(), 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
}
/// <summary>
/// Creates a new data reader
/// </summary>
/// <param name="rva">RVA of data</param>
/// <param name="size">Size of data</param>
/// <returns></returns>
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<byte>(), filename: null);
dataOffset = 0;
dataLength = 0;
}
}
/// <inheritdoc/>
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);
}
}
}