From d3f9e4aaa2bb440250ab51e667100c3345e30587 Mon Sep 17 00:00:00 2001 From: Lloyd Kinsella Date: Wed, 8 Aug 2018 12:17:51 +0100 Subject: [PATCH] Started work on resources. --- Src/Workshell.PE/Content/CLR/CLR.cs | 2 +- Src/Workshell.PE/DataDirectory.cs | 2 + src/Workshell.PE.Testbed/Program.cs | 11 +- src/Workshell.PE/Content/Certificate.cs | 2 +- src/Workshell.PE/Content/DataContent.cs | 6 +- .../Content/Debug/DebugDirectory.cs | 2 +- .../Content/Exceptions/ExceptionTable.cs | 2 +- .../Content/Exceptions/ExceptionTable32.cs | 2 +- .../Content/Exceptions/ExceptionTable64.cs | 2 +- .../Content/Exports/ExportTable.cs | 2 +- .../Imports/DelayedImportAddressTables.cs | 4 +- .../Content/Imports/DelayedImportDirectory.cs | 2 +- .../Imports/DelayedImportHintNameTable.cs | 4 +- .../Content/Imports/ImportAddressTables.cs | 4 +- .../Imports/ImportAddressTablesBase.cs | 2 +- .../Content/Imports/ImportDirectory.cs | 2 +- .../Content/Imports/ImportDirectoryBase.cs | 2 +- .../Content/Imports/ImportHintNameTable.cs | 4 +- .../Imports/ImportHintNameTableBase.cs | 2 +- .../Content/Relocation/RelocationTable.cs | 2 +- .../Content/Resources/ResourceData.cs | 38 ++++ .../Content/Resources/ResourceDataEntry.cs | 85 +++++++++ .../Content/Resources/ResourceDirectory.cs | 162 +++++++++++++++++ .../Resources/ResourceDirectoryEntry.cs | 165 ++++++++++++++++++ .../Native/IMAGE_RESOURCE_DATA_ENTRY.cs | 16 ++ .../Native/IMAGE_RESOURCE_DIRECTORY.cs | 18 ++ .../Native/IMAGE_RESOURCE_DIRECTORY_ENTRY.cs | 14 ++ 27 files changed, 526 insertions(+), 33 deletions(-) create mode 100644 src/Workshell.PE/Content/Resources/ResourceData.cs create mode 100644 src/Workshell.PE/Content/Resources/ResourceDataEntry.cs create mode 100644 src/Workshell.PE/Content/Resources/ResourceDirectory.cs create mode 100644 src/Workshell.PE/Content/Resources/ResourceDirectoryEntry.cs create mode 100644 src/Workshell.PE/Native/IMAGE_RESOURCE_DATA_ENTRY.cs create mode 100644 src/Workshell.PE/Native/IMAGE_RESOURCE_DIRECTORY.cs create mode 100644 src/Workshell.PE/Native/IMAGE_RESOURCE_DIRECTORY_ENTRY.cs diff --git a/Src/Workshell.PE/Content/CLR/CLR.cs b/Src/Workshell.PE/Content/CLR/CLR.cs index 35cfe84..dcc16ba 100644 --- a/Src/Workshell.PE/Content/CLR/CLR.cs +++ b/Src/Workshell.PE/Content/CLR/CLR.cs @@ -7,7 +7,7 @@ namespace Workshell.PE.Content { public sealed class CLR : DataContent { - private CLR(PortableExecutableImage image, DataDirectory directory, Location location, CLRHeader header, CLRMetaData metaData) : base(image, directory, location) + private CLR(PortableExecutableImage image, DataDirectory dataDirectory, Location location, CLRHeader header, CLRMetaData metaData) : base(image, dataDirectory, location) { Header = header; MetaData = metaData; diff --git a/Src/Workshell.PE/DataDirectory.cs b/Src/Workshell.PE/DataDirectory.cs index bfebb28..7b52c8f 100644 --- a/Src/Workshell.PE/DataDirectory.cs +++ b/Src/Workshell.PE/DataDirectory.cs @@ -109,6 +109,8 @@ public async Task GetContentAsync() return await DelayedImportDirectory.GetAsync(_image).ConfigureAwait(false); case DataDirectoryType.ExceptionTable: return await ExceptionTable.GetAsync(_image).ConfigureAwait(false); + case DataDirectoryType.ResourceTable: + return await ResourceDirectory.GetAsync(_image).ConfigureAwait(false); default: { var calc = _image.GetCalculator(); diff --git a/src/Workshell.PE.Testbed/Program.cs b/src/Workshell.PE.Testbed/Program.cs index bcdef4a..8093778 100644 --- a/src/Workshell.PE.Testbed/Program.cs +++ b/src/Workshell.PE.Testbed/Program.cs @@ -17,17 +17,10 @@ static async Task RunAsync(string[] args) { //var image = await PortableExecutableImage.FromFileAsync(@"C:\Users\lkinsella\Downloads\IISCrypto.exe"); var image = await PortableExecutableImage.FromFileAsync(@"C:\Windows\System32\shell32.dll"); - var dataDirectory = image.NTHeaders.DataDirectories[DataDirectoryType.DelayImportDescriptor]; + var dataDirectory = image.NTHeaders.DataDirectories[DataDirectoryType.ResourceTable]; var content = await dataDirectory.GetContentAsync().ConfigureAwait(false); - var table = await ExceptionTable.GetAsync(image); - var entries = table.Cast().ToArray(); - var entry = entries[41]; - //var entry = entries[6506]; - var info = await entry.GetUnwindInfoAsync(); - - //var table = await ImportHintNameTable.GetAsync(image).ConfigureAwait(false); - //var entries = table.ToArray(); + } } } diff --git a/src/Workshell.PE/Content/Certificate.cs b/src/Workshell.PE/Content/Certificate.cs index 28678c9..1a7cfbc 100644 --- a/src/Workshell.PE/Content/Certificate.cs +++ b/src/Workshell.PE/Content/Certificate.cs @@ -20,7 +20,7 @@ public enum CertificateType : ushort public sealed class Certificate : DataContent { - private Certificate(PortableExecutableImage image, DataDirectory directory, Location location, WIN_CERTIFICATE cert) : base(image, directory, location) + private Certificate(PortableExecutableImage image, DataDirectory dataDirectory, Location location, WIN_CERTIFICATE cert) : base(image, dataDirectory, location) { Length = cert.dwLength; Revision = cert.wRevision; diff --git a/src/Workshell.PE/Content/DataContent.cs b/src/Workshell.PE/Content/DataContent.cs index 9898308..6d846e9 100644 --- a/src/Workshell.PE/Content/DataContent.cs +++ b/src/Workshell.PE/Content/DataContent.cs @@ -8,9 +8,9 @@ namespace Workshell.PE.Content { public class DataContent : ISupportsLocation, ISupportsBytes { - public DataContent(PortableExecutableImage image, DataDirectory directory, Location location) + public DataContent(PortableExecutableImage image, DataDirectory dataDirectory, Location location) { - Directory = directory; + DataDirectory = dataDirectory; Location = location; Image = image; } @@ -34,7 +34,7 @@ public async Task GetBytesAsync() #region Properties - public DataDirectory Directory { get; } + public DataDirectory DataDirectory { get; } public Location Location { get; } protected PortableExecutableImage Image { get; } diff --git a/src/Workshell.PE/Content/Debug/DebugDirectory.cs b/src/Workshell.PE/Content/Debug/DebugDirectory.cs index b227e9a..68ecfd7 100644 --- a/src/Workshell.PE/Content/Debug/DebugDirectory.cs +++ b/src/Workshell.PE/Content/Debug/DebugDirectory.cs @@ -15,7 +15,7 @@ public sealed class DebugDirectory : DataContent, IEnumerable { - internal DelayedImportAddressTables(PortableExecutableImage image, DataDirectory directory, Location location, Tuple[] tables) : base(image, directory, location, tables, false) + internal DelayedImportAddressTables(PortableExecutableImage image, DataDirectory dataDirectory, Location location, Tuple[] tables) : base(image, dataDirectory, location, tables, false) { } @@ -81,7 +81,7 @@ private static async Task GetTableAsync(PortableExec var section = calc.RVAToSection(rva); var location = new Location(fileOffset, rva, va, fileSize, fileSize, section); - var result = new DelayedImportAddressTables(image, directory.Directory, location, tables.ToArray()); + var result = new DelayedImportAddressTables(image, directory.DataDirectory, location, tables.ToArray()); return result; } diff --git a/src/Workshell.PE/Content/Imports/DelayedImportDirectory.cs b/src/Workshell.PE/Content/Imports/DelayedImportDirectory.cs index 1c5a22d..e9a3fba 100644 --- a/src/Workshell.PE/Content/Imports/DelayedImportDirectory.cs +++ b/src/Workshell.PE/Content/Imports/DelayedImportDirectory.cs @@ -12,7 +12,7 @@ namespace Workshell.PE.Content { public sealed class DelayedImportDirectory : ImportDirectoryBase { - private DelayedImportDirectory(PortableExecutableImage image, DataDirectory directory, Location location, DelayedImportDirectoryEntry[] entries) : base(image, directory, location, entries) + private DelayedImportDirectory(PortableExecutableImage image, DataDirectory dataDirectory, Location location, DelayedImportDirectoryEntry[] entries) : base(image, dataDirectory, location, entries) { } diff --git a/src/Workshell.PE/Content/Imports/DelayedImportHintNameTable.cs b/src/Workshell.PE/Content/Imports/DelayedImportHintNameTable.cs index a7b9dab..98fdf75 100644 --- a/src/Workshell.PE/Content/Imports/DelayedImportHintNameTable.cs +++ b/src/Workshell.PE/Content/Imports/DelayedImportHintNameTable.cs @@ -10,7 +10,7 @@ namespace Workshell.PE.Content { public sealed class DelayedImportHintNameTable : ImportHintNameTableBase { - internal DelayedImportHintNameTable(PortableExecutableImage image, DataDirectory directory, Location location, Tuple[] entries) : base(image, directory, location, entries, true) + internal DelayedImportHintNameTable(PortableExecutableImage image, DataDirectory dataDirectory, Location location, Tuple[] entries) : base(image, dataDirectory, location, entries, true) { } @@ -93,7 +93,7 @@ public static async Task GetAsync(PortableExecutable location = new Location(0, 0, 0, 0, 0, null); } - var result = new DelayedImportHintNameTable(image, directory.Directory, location, entries.Values.ToArray()); + var result = new DelayedImportHintNameTable(image, directory.DataDirectory, location, entries.Values.ToArray()); return result; } diff --git a/src/Workshell.PE/Content/Imports/ImportAddressTables.cs b/src/Workshell.PE/Content/Imports/ImportAddressTables.cs index 4adcf94..44c051e 100644 --- a/src/Workshell.PE/Content/Imports/ImportAddressTables.cs +++ b/src/Workshell.PE/Content/Imports/ImportAddressTables.cs @@ -10,7 +10,7 @@ namespace Workshell.PE.Content { public sealed class ImportAddressTables: ImportAddressTablesBase { - internal ImportAddressTables(PortableExecutableImage image, DataDirectory directory, Location location, Tuple[] tables) : base(image, directory, location, tables, false) + internal ImportAddressTables(PortableExecutableImage image, DataDirectory dataDirectory, Location location, Tuple[] tables) : base(image, dataDirectory, location, tables, false) { } @@ -81,7 +81,7 @@ private static async Task GetTableAsync(PortableExecutableI var section = calc.RVAToSection(rva); var location = new Location(fileOffset, rva, va, fileSize, fileSize, section); - var result = new ImportAddressTables(image, directory.Directory, location, tables.ToArray()); + var result = new ImportAddressTables(image, directory.DataDirectory, location, tables.ToArray()); return result; } diff --git a/src/Workshell.PE/Content/Imports/ImportAddressTablesBase.cs b/src/Workshell.PE/Content/Imports/ImportAddressTablesBase.cs index 791e3ce..6c538eb 100644 --- a/src/Workshell.PE/Content/Imports/ImportAddressTablesBase.cs +++ b/src/Workshell.PE/Content/Imports/ImportAddressTablesBase.cs @@ -13,7 +13,7 @@ public abstract class ImportAddressTablesBase : DataContent { private readonly TTable[] _tables; - protected internal ImportAddressTablesBase(PortableExecutableImage image, DataDirectory directory, Location location, Tuple[] tables, bool isDelayed) : base(image, directory, location) + protected internal ImportAddressTablesBase(PortableExecutableImage image, DataDirectory dataDirectory, Location location, Tuple[] tables, bool isDelayed) : base(image, dataDirectory, location) { _tables = new TTable[tables.Length]; diff --git a/src/Workshell.PE/Content/Imports/ImportDirectory.cs b/src/Workshell.PE/Content/Imports/ImportDirectory.cs index 5989a42..11e4671 100644 --- a/src/Workshell.PE/Content/Imports/ImportDirectory.cs +++ b/src/Workshell.PE/Content/Imports/ImportDirectory.cs @@ -12,7 +12,7 @@ namespace Workshell.PE.Content { public sealed class ImportDirectory : ImportDirectoryBase { - private ImportDirectory(PortableExecutableImage image, DataDirectory directory, Location location, ImportDirectoryEntry[] entries) : base(image, directory, location, entries) + private ImportDirectory(PortableExecutableImage image, DataDirectory dataDirectory, Location location, ImportDirectoryEntry[] entries) : base(image, dataDirectory, location, entries) { } diff --git a/src/Workshell.PE/Content/Imports/ImportDirectoryBase.cs b/src/Workshell.PE/Content/Imports/ImportDirectoryBase.cs index fdd8554..d2303ab 100644 --- a/src/Workshell.PE/Content/Imports/ImportDirectoryBase.cs +++ b/src/Workshell.PE/Content/Imports/ImportDirectoryBase.cs @@ -13,7 +13,7 @@ public abstract class ImportDirectoryBase : DataContent, IEnumerable where { private readonly T[] _entries; - protected internal ImportDirectoryBase(PortableExecutableImage image, DataDirectory directory, Location location, T[] entries) : base(image, directory, location) + protected internal ImportDirectoryBase(PortableExecutableImage image, DataDirectory dataDirectory, Location location, T[] entries) : base(image, dataDirectory, location) { _entries = entries; diff --git a/src/Workshell.PE/Content/Imports/ImportHintNameTable.cs b/src/Workshell.PE/Content/Imports/ImportHintNameTable.cs index 490feea..911f834 100644 --- a/src/Workshell.PE/Content/Imports/ImportHintNameTable.cs +++ b/src/Workshell.PE/Content/Imports/ImportHintNameTable.cs @@ -10,7 +10,7 @@ namespace Workshell.PE.Content { public sealed class ImportHintNameTable : ImportHintNameTableBase { - internal ImportHintNameTable(PortableExecutableImage image, DataDirectory directory, Location location, Tuple[] entries) : base(image, directory, location, entries, false) + internal ImportHintNameTable(PortableExecutableImage image, DataDirectory dataDirectory, Location location, Tuple[] entries) : base(image, dataDirectory, location, entries, false) { } @@ -93,7 +93,7 @@ public static async Task GetAsync(PortableExecutableImage i location = new Location(0, 0, 0, 0, 0, null); } - var result = new ImportHintNameTable(image, directory.Directory, location, entries.Values.ToArray()); + var result = new ImportHintNameTable(image, directory.DataDirectory, location, entries.Values.ToArray()); return result; } diff --git a/src/Workshell.PE/Content/Imports/ImportHintNameTableBase.cs b/src/Workshell.PE/Content/Imports/ImportHintNameTableBase.cs index f4fc5d4..09ebe66 100644 --- a/src/Workshell.PE/Content/Imports/ImportHintNameTableBase.cs +++ b/src/Workshell.PE/Content/Imports/ImportHintNameTableBase.cs @@ -11,7 +11,7 @@ public abstract class ImportHintNameTableBase : DataContent, IEnumerable { private readonly TEntry[] _entries; - protected internal ImportHintNameTableBase(PortableExecutableImage image, DataDirectory directory, Location location, Tuple[] entries, bool isDelayed) : base(image, directory, location) + protected internal ImportHintNameTableBase(PortableExecutableImage image, DataDirectory dataDirectory, Location location, Tuple[] entries, bool isDelayed) : base(image, dataDirectory, location) { _entries = BuildTable(entries); diff --git a/src/Workshell.PE/Content/Relocation/RelocationTable.cs b/src/Workshell.PE/Content/Relocation/RelocationTable.cs index cb3a03c..4570d3d 100644 --- a/src/Workshell.PE/Content/Relocation/RelocationTable.cs +++ b/src/Workshell.PE/Content/Relocation/RelocationTable.cs @@ -13,7 +13,7 @@ public sealed class RelocationTable : DataContent, IEnumerable { private readonly RelocationBlock[] _blocks; - private RelocationTable(PortableExecutableImage image, DataDirectory directory, Location location, RelocationBlock[] blocks) : base(image, directory, location) + private RelocationTable(PortableExecutableImage image, DataDirectory dataDirectory, Location location, RelocationBlock[] blocks) : base(image, dataDirectory, location) { _blocks = blocks; diff --git a/src/Workshell.PE/Content/Resources/ResourceData.cs b/src/Workshell.PE/Content/Resources/ResourceData.cs new file mode 100644 index 0000000..8d8c6d7 --- /dev/null +++ b/src/Workshell.PE/Content/Resources/ResourceData.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +namespace Workshell.PE.Content +{ + public sealed class ResourceData : DataContent + { + internal ResourceData(PortableExecutableImage image, DataDirectory dataDirectory, Location location, ResourceDataEntry entry) : base(image, dataDirectory, location) + { + Entry = entry; + } + + #region Methods + + public void CopyTo(Stream stream) + { + CopyToAsync(stream).GetAwaiter().GetResult(); + } + + public async Task CopyToAsync(Stream stream) + { + var buffer = await GetBytesAsync().ConfigureAwait(false); + + await stream.WriteAsync(buffer, 0, buffer.Length).ConfigureAwait(false); + } + + #endregion + + #region Properties + + public ResourceDataEntry Entry { get; } + + #endregion + } +} diff --git a/src/Workshell.PE/Content/Resources/ResourceDataEntry.cs b/src/Workshell.PE/Content/Resources/ResourceDataEntry.cs new file mode 100644 index 0000000..b4097ce --- /dev/null +++ b/src/Workshell.PE/Content/Resources/ResourceDataEntry.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; + +using Workshell.PE.Annotations; +using Workshell.PE.Extensions; +using Workshell.PE.Native; + +namespace Workshell.PE.Content +{ + public sealed class ResourceDataEntry : DataContent + { + private ResourceData _data; + + internal ResourceDataEntry(PortableExecutableImage image, DataDirectory dataDirectory, Location location, ResourceDirectoryEntry directoryEntry) : base(image, dataDirectory, location) + { + _data = null; + + DirectoryEntry = directoryEntry; + } + + #region Methods + + public ResourceData GetData() + { + if (_data == null) + { + var calc = Image.GetCalculator(); + var rva = OffsetToData; + var va = Image.NTHeaders.OptionalHeader.ImageBase + rva; + var offset = calc.RVAToOffset(rva); + var size = Size; + var section = calc.RVAToSection(rva); + var location = new Location(offset, rva, va, size, size, section); + + _data = new ResourceData(Image, DataDirectory, location, this); + } + + return _data; + } + + internal async Task LoadAsync() + { + var stream = Image.GetStream(); + + stream.Seek(Location.FileOffset.ToInt64(), SeekOrigin.Begin); + + try + { + var entry = await stream.ReadStructAsync().ConfigureAwait(false); + + OffsetToData = entry.OffsetToData; + Size = entry.Size; + CodePage = entry.CodePage; + Reserved = entry.Reserved; + } + catch (Exception ex) + { + throw new PortableExecutableImageException(Image, "Could not read resource data entry from stream.", ex); + } + } + + #endregion + + #region Properties + + public ResourceDirectoryEntry DirectoryEntry { get; } + + [FieldAnnotation("Offset to Data")] + public uint OffsetToData { get; private set; } + + [FieldAnnotation("Size")] + public uint Size { get; private set; } + + [FieldAnnotation("Code Page")] + public uint CodePage { get; private set; } + + [FieldAnnotation("Reserved")] + public uint Reserved { get; private set; } + + #endregion + } +} diff --git a/src/Workshell.PE/Content/Resources/ResourceDirectory.cs b/src/Workshell.PE/Content/Resources/ResourceDirectory.cs new file mode 100644 index 0000000..ba0a066 --- /dev/null +++ b/src/Workshell.PE/Content/Resources/ResourceDirectory.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using Workshell.PE.Annotations; +using Workshell.PE.Extensions; +using Workshell.PE.Native; + +namespace Workshell.PE.Content +{ + public sealed class ResourceDirectory : DataContent, IEnumerable + { + private ResourceDirectoryEntry[] _entries; + + internal ResourceDirectory(PortableExecutableImage image, DataDirectory dataDirectory, Location location, ResourceDirectoryEntry directoryEntry) : base(image, dataDirectory, location) + { + _entries = new ResourceDirectoryEntry[0]; + + DirectoryEntry = directoryEntry; + } + + #region Static Methods + + public static ResourceDirectory Get(PortableExecutableImage image) + { + return GetAsync(image).GetAwaiter().GetResult(); + } + + public static async Task GetAsync(PortableExecutableImage image) + { + if (!image.NTHeaders.DataDirectories.Exists(DataDirectoryType.ResourceTable)) + return null; + + var dataDirectory = image.NTHeaders.DataDirectories[DataDirectoryType.ResourceTable]; + + if (DataDirectory.IsNullOrEmpty(dataDirectory)) + return null; + + var calc = image.GetCalculator(); + var rva = dataDirectory.VirtualAddress; + var va = image.NTHeaders.OptionalHeader.ImageBase + rva; + var offset = calc.RVAToOffset(rva); + var size = Marshal.SizeOf().ToUInt32(); + var section = calc.RVAToSection(rva); + var location = new Location(offset, rva, va, size, size, section); + var directory = new ResourceDirectory(image, dataDirectory, location, null); + + await directory.LoadAsync().ConfigureAwait(false); + + return directory; + } + + #endregion + + #region Methods + + public IEnumerator GetEnumerator() + { + foreach (var entry in _entries) + yield return entry; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public DateTime GetTimeDateStamp() + { + return Utils.ConvertTimeDateStamp(TimeDateStamp); + } + + public Version GetVersion() + { + return new Version(MajorVersion, MinorVersion); + } + + internal async Task LoadAsync() + { + + var calc = Image.GetCalculator(); + var stream = Image.GetStream(); + + stream.Seek(Location.FileOffset.ToInt64(),SeekOrigin.Begin); + + var directorySize = Marshal.SizeOf(); + IMAGE_RESOURCE_DIRECTORY directory; + + try + { + directory = await stream.ReadStructAsync(directorySize).ConfigureAwait(false); + } + catch (Exception ex) + { + throw new PortableExecutableImageException(Image, "Could not read resource directory from stream.", ex); + } + + var count = directory.NumberOfNamedEntries + directory.NumberOfIdEntries; + var entries = new List(count); + var offset = Location.FileOffset + directorySize.ToUInt32(); + var entrySize = Marshal.SizeOf(); + + for (int i = 0; i < count; i++) + { + stream.Seek(offset.ToInt64(),SeekOrigin.Begin); + + var entry = await stream.ReadStructAsync(entrySize).ConfigureAwait(false); + var rva = calc.OffsetToRVA(offset); + var va = Image.NTHeaders.OptionalHeader.ImageBase + rva; + var section = calc.RVAToSection(rva); + var location = new Location(offset, rva, va, entrySize.ToUInt32(), entrySize.ToUInt32(), section); + var directoryEntry = new ResourceDirectoryEntry(Image, DataDirectory, location, this, entry); + + entries.Add(directoryEntry); + + offset += entrySize.ToUInt32(); + } + + _entries = entries.ToArray(); + + Characteristics = directory.Characteristics; + TimeDateStamp = directory.TimeDateStamp; + MajorVersion = directory.MajorVersion; + MinorVersion = directory.MinorVersion; + NumberOfNamedEntries = directory.NumberOfNamedEntries; + NumberOfIdEntries = directory.NumberOfIdEntries; + } + + #endregion + + #region Properties + + public ResourceDirectoryEntry DirectoryEntry { get; } + public int Count => _entries.Length; + public ResourceDirectoryEntry this[int index] => _entries[index]; + + [FieldAnnotation("Characteristics")] + public uint Characteristics { get; private set; } + + [FieldAnnotation("Date/Time Stamp")] + public uint TimeDateStamp { get; private set; } + + [FieldAnnotation("Major Version")] + public ushort MajorVersion { get; private set; } + + [FieldAnnotation("Minor Version")] + public ushort MinorVersion { get; private set; } + + [FieldAnnotation("Number of Named Entries")] + public ushort NumberOfNamedEntries { get; private set; } + + [FieldAnnotation("Number of ID Entries")] + public ushort NumberOfIdEntries { get; private set; } + + + + #endregion + } +} diff --git a/src/Workshell.PE/Content/Resources/ResourceDirectoryEntry.cs b/src/Workshell.PE/Content/Resources/ResourceDirectoryEntry.cs new file mode 100644 index 0000000..a23d6e2 --- /dev/null +++ b/src/Workshell.PE/Content/Resources/ResourceDirectoryEntry.cs @@ -0,0 +1,165 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; + +using Workshell.PE.Annotations; +using Workshell.PE.Extensions; +using Workshell.PE.Native; + +namespace Workshell.PE.Content +{ + public enum NameType + { + ID, + Name + } + + public enum OffsetType + { + Directory, + Data + } + + public sealed class ResourceDirectoryEntry : DataContent + { + private string _name; + private ResourceDirectory _directory; + private ResourceDataEntry _data; + + internal ResourceDirectoryEntry(PortableExecutableImage image, DataDirectory dataDirectory, Location location, ResourceDirectory parentDirectory, IMAGE_RESOURCE_DIRECTORY_ENTRY entry) : base(image, dataDirectory, location) + { + _name = null; + _directory = null; + _data = null; + + ParentDirectory = parentDirectory; + NameType = ((entry.Name & 0x80000000) == 0x80000000 ? NameType.Name : NameType.ID); + OffsetType = ((entry.OffsetToData & 0x80000000) == 0x80000000 ? OffsetType.Directory : OffsetType.Data); + + Name = entry.Name; + OffsetToData = entry.OffsetToData; + } + + #region Methods + + public uint GetId() + { + if ((Name & 0x80000000) == 0x80000000) + return 0; + + return Name; + } + + public string GetName() + { + return GetNameAsync().GetAwaiter().GetResult(); + } + + public async Task GetNameAsync() + { + if (_name == null) + { + if ((Name & 0x80000000) == 0x80000000) + { + var calc = Image.GetCalculator(); + var stream = Image.GetStream(); + var offset = Name & 0x7fffffff; + var rva = DataDirectory.VirtualAddress + offset; + var fileOffset = calc.RVAToOffset(rva); + + stream.Seek(fileOffset.ToInt64(), SeekOrigin.Begin); + + var count = await stream.ReadUInt16Async().ConfigureAwait(false); + var builder = new StringBuilder(count); + + for(int i = 0; i < count; i++) + { + var value = await stream.ReadUInt16Async().ConfigureAwait(false); + var chr = Convert.ToChar(value); + + builder.Append(chr); + } + + _name = builder.ToString(); + } + else + { + _name = string.Empty; + } + } + + return _name; + } + + public ResourceDirectory GetDirectory() + { + return GetDirectoryAsync().GetAwaiter().GetResult(); + } + + public async Task GetDirectoryAsync() + { + if (_directory == null && (OffsetToData & 0x80000000) == 0x80000000) + { + var calc = Image.GetCalculator(); + var offset = OffsetToData & 0x7fffffff; + var rva = DataDirectory.VirtualAddress + offset; + var va = Image.NTHeaders.OptionalHeader.ImageBase + rva; + var fileOffset = calc.RVAToOffset(rva); + var size = Marshal.SizeOf().ToUInt32(); + var section = calc.RVAToSection(rva); + var location = new Location(fileOffset, rva, va, size, size, section); + + _directory = new ResourceDirectory(Image, DataDirectory, location, this); + + await _directory.LoadAsync().ConfigureAwait(false); + } + + return _directory; + } + + public ResourceDataEntry GetDataEntry() + { + return GetDataEntryAsync().GetAwaiter().GetResult(); + } + + public async Task GetDataEntryAsync() + { + if (_data == null && (OffsetToData & 0x80000000) != 0x80000000) + { + var calc = Image.GetCalculator(); + var offset = OffsetToData & 0x7fffffff; + var rva = DataDirectory.VirtualAddress + offset; + var va = Image.NTHeaders.OptionalHeader.ImageBase + rva; + var fileOffset = calc.RVAToOffset(rva); + var size = Marshal.SizeOf().ToUInt32(); + var section = calc.RVAToSection(rva); + var location = new Location(fileOffset, rva, va, size, size, section); + + _data = new ResourceDataEntry(Image, DataDirectory, location, this); + + await _data.LoadAsync().ConfigureAwait(false); + } + + return _data; + } + + #endregion + + #region Properties + + public ResourceDirectory ParentDirectory { get; } + public NameType NameType { get; } + public OffsetType OffsetType { get; } + + [FieldAnnotation("Name")] + public uint Name { get; } + + [FieldAnnotation("Offset to Data")] + public uint OffsetToData { get; } + + #endregion + } +} diff --git a/src/Workshell.PE/Native/IMAGE_RESOURCE_DATA_ENTRY.cs b/src/Workshell.PE/Native/IMAGE_RESOURCE_DATA_ENTRY.cs new file mode 100644 index 0000000..6137c06 --- /dev/null +++ b/src/Workshell.PE/Native/IMAGE_RESOURCE_DATA_ENTRY.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace Workshell.PE.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct IMAGE_RESOURCE_DATA_ENTRY + { + public uint OffsetToData; + public uint Size; + public uint CodePage; + public uint Reserved; + } +} diff --git a/src/Workshell.PE/Native/IMAGE_RESOURCE_DIRECTORY.cs b/src/Workshell.PE/Native/IMAGE_RESOURCE_DIRECTORY.cs new file mode 100644 index 0000000..7461704 --- /dev/null +++ b/src/Workshell.PE/Native/IMAGE_RESOURCE_DIRECTORY.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace Workshell.PE.Native +{ + [StructLayout(LayoutKind.Sequential)] + internal struct IMAGE_RESOURCE_DIRECTORY + { + public uint Characteristics; + public uint TimeDateStamp; + public ushort MajorVersion; + public ushort MinorVersion; + public ushort NumberOfNamedEntries; + public ushort NumberOfIdEntries; + } +} diff --git a/src/Workshell.PE/Native/IMAGE_RESOURCE_DIRECTORY_ENTRY.cs b/src/Workshell.PE/Native/IMAGE_RESOURCE_DIRECTORY_ENTRY.cs new file mode 100644 index 0000000..9991367 --- /dev/null +++ b/src/Workshell.PE/Native/IMAGE_RESOURCE_DIRECTORY_ENTRY.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace Workshell.PE.Native +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + internal struct IMAGE_RESOURCE_DIRECTORY_ENTRY + { + public uint Name; + public uint OffsetToData; + } +}