From 13a6bb08da6875b82e4251e9e3699b81aca97a14 Mon Sep 17 00:00:00 2001 From: Lloyd Kinsella Date: Tue, 5 Nov 2019 23:44:51 +0000 Subject: [PATCH] Lots of work coming out of PEI. --- Src/Workshell.PE/Content/CLR/CLRHeader.cs | 8 +- Src/Workshell.PE/Location.cs | 1 - Src/Workshell.PE/SectionTable.cs | 15 + .../Content/Debug/DebugDirectoryEntry.cs | 1 - .../Content/Exports/ExportTable.cs | 27 ++ .../Imports/DelayedImportAddressTable.cs | 73 ++-- .../Imports/DelayedImportAddressTables.cs | 40 +- .../Imports/DelayedImportDirectoryEntry.cs | 8 +- .../Imports/DelayedImportHintNameTable.cs | 9 +- .../Content/Imports/DelayedImports.cs | 2 +- .../Imports/ImportDirectoryEntryBase.cs | 168 ++++---- .../Content/Imports/ImportLibraryBase.cs | 180 ++++----- .../Content/Imports/ImportLibraryFunction.cs | 196 +++++----- .../Content/Imports/ImportsBase.cs | 132 +++---- .../Content/Relocation/RelocationTable.cs | 8 + .../Content/Resources/Resource.cs | 364 +++++++++--------- 16 files changed, 675 insertions(+), 557 deletions(-) diff --git a/Src/Workshell.PE/Content/CLR/CLRHeader.cs b/Src/Workshell.PE/Content/CLR/CLRHeader.cs index 2413792..25bcb51 100644 --- a/Src/Workshell.PE/Content/CLR/CLRHeader.cs +++ b/Src/Workshell.PE/Content/CLR/CLRHeader.cs @@ -207,10 +207,10 @@ public CLRDataDirectory GetManagedNativeHeader() [FieldAnnotation("Minor Runtime Version", Order = 3)] public ushort MinorRuntimeVersion { get; } - [FieldAnnotation("MetaData Virtual Address", Order = 4)] + [FieldAnnotation("Meta-data Virtual Address", Order = 4)] public uint MetaDataAddress { get; } - [FieldAnnotation("MetaData Size", Order = 5)] + [FieldAnnotation("Meta-data Size", Order = 5)] public uint MetaDataSize { get; } [FieldAnnotation("Flags", Order = 6)] @@ -225,10 +225,10 @@ public CLRDataDirectory GetManagedNativeHeader() [FieldAnnotation("Resources Size", Order = 9)] public uint ResourcesSize { get; } - [FieldAnnotation("Strongname Signature Virtual Address", Order = 10)] + [FieldAnnotation("Strong-name Signature Virtual Address", Order = 10)] public uint StrongNameSignatureAddress { get; } - [FieldAnnotation("Strongname Signature Size", Order = 11)] + [FieldAnnotation("Strong-name Signature Size", Order = 11)] public uint StrongNameSignatureSize { get; } [FieldAnnotation("Code Manager Table Virtual Address", Order = 12)] diff --git a/Src/Workshell.PE/Location.cs b/Src/Workshell.PE/Location.cs index c733f53..f4abf69 100644 --- a/Src/Workshell.PE/Location.cs +++ b/Src/Workshell.PE/Location.cs @@ -48,7 +48,6 @@ internal Location(PortableExecutableImage image, long fileOffset, uint rva, ulon VirtualSize = virtualSize; } - #region Methods public override string ToString() diff --git a/Src/Workshell.PE/SectionTable.cs b/Src/Workshell.PE/SectionTable.cs index c770d9f..35864b8 100644 --- a/Src/Workshell.PE/SectionTable.cs +++ b/Src/Workshell.PE/SectionTable.cs @@ -232,6 +232,19 @@ public SectionCharacteristicsType GetCharacteristics() return (SectionCharacteristicsType)_header.Characteristics; } + public Section GetSection() + { + foreach(var section in _image.Sections) + { + if (section.TableEntry == this) + { + return section; + } + } + + return null; + } + private string GetName() { var builder = new StringBuilder(16); @@ -257,6 +270,8 @@ private string GetName() public string Name { get; } + public bool IsEmpty => (_header.SizeOfRawData == 0); + public uint VirtualSizeOrPhysicalAddress => _header.VirtualSize; public uint VirtualAddress => _header.VirtualAddress; public uint SizeOfRawData => _header.SizeOfRawData; diff --git a/src/Workshell.PE/Content/Debug/DebugDirectoryEntry.cs b/src/Workshell.PE/Content/Debug/DebugDirectoryEntry.cs index 3426d09..f63fcd7 100644 --- a/src/Workshell.PE/Content/Debug/DebugDirectoryEntry.cs +++ b/src/Workshell.PE/Content/Debug/DebugDirectoryEntry.cs @@ -134,7 +134,6 @@ public DebugData GetData() return null; } - var calc = _image.GetCalculator(); var rva = AddressOfRawData; var imageBase = _image.NTHeaders.OptionalHeader.ImageBase; var location = new Location(_image, PointerToRawData, rva, imageBase + rva, SizeOfData, SizeOfData); diff --git a/src/Workshell.PE/Content/Exports/ExportTable.cs b/src/Workshell.PE/Content/Exports/ExportTable.cs index 89486a4..ce7c730 100644 --- a/src/Workshell.PE/Content/Exports/ExportTable.cs +++ b/src/Workshell.PE/Content/Exports/ExportTable.cs @@ -38,10 +38,27 @@ protected ExportTable(PortableExecutableImage image, DataDirectory dataDirectory #region Static Methods + public static ExportTable GetFunctionAddressTable(PortableExecutableImage image, ExportDirectory directory = null) + { + return GetFunctionAddressTableAsync(image, directory).GetAwaiter().GetResult(); + } + + public static ExportTable GetNameAddressTable(PortableExecutableImage image, ExportDirectory directory = null) + { + return GetNameAddressTableAsync(image, directory).GetAwaiter().GetResult(); + } + + public static ExportTable GetOrdinalTable(PortableExecutableImage image, ExportDirectory directory = null) + { + return GetOrdinalTableAsync(image, directory).GetAwaiter().GetResult(); + } + public static async Task> GetFunctionAddressTableAsync(PortableExecutableImage image, ExportDirectory directory = null) { if (directory == null) + { directory = await ExportDirectory.GetAsync(image).ConfigureAwait(false); + } var calc = image.GetCalculator(); var section = calc.RVAToSection(directory.AddressOfFunctions); @@ -58,7 +75,9 @@ public static async Task> GetFunctionAddressTableAsync(Portabl try { for (var i = 0; i < directory.NumberOfFunctions; i++) + { addresses[i] = await stream.ReadUInt32Async().ConfigureAwait(false); + } } catch (Exception ex) { @@ -75,7 +94,9 @@ public static async Task> GetFunctionAddressTableAsync(Portabl public static async Task> GetNameAddressTableAsync(PortableExecutableImage image, ExportDirectory directory = null) { if (directory == null) + { directory = await ExportDirectory.GetAsync(image).ConfigureAwait(false); + } var calc = image.GetCalculator(); var section = calc.RVAToSection(directory.AddressOfNames); @@ -92,7 +113,9 @@ public static async Task> GetNameAddressTableAsync(PortableExe try { for (var i = 0; i < directory.NumberOfNames; i++) + { addresses[i] = await stream.ReadUInt32Async().ConfigureAwait(false); + } } catch (Exception ex) { @@ -109,7 +132,9 @@ public static async Task> GetNameAddressTableAsync(PortableExe public static async Task> GetOrdinalTableAsync(PortableExecutableImage image, ExportDirectory directory = null) { if (directory == null) + { directory = await ExportDirectory.GetAsync(image).ConfigureAwait(false); + } var calc = image.GetCalculator(); var section = calc.RVAToSection(directory.AddressOfNameOrdinals); @@ -126,7 +151,9 @@ public static async Task> GetOrdinalTableAsync(PortableExecu try { for (var i = 0; i < directory.NumberOfNames; i++) + { ordinals[i] = await stream.ReadUInt16Async().ConfigureAwait(false); + } } catch (Exception ex) { diff --git a/src/Workshell.PE/Content/Imports/DelayedImportAddressTable.cs b/src/Workshell.PE/Content/Imports/DelayedImportAddressTable.cs index 050e6b6..b89ef2f 100644 --- a/src/Workshell.PE/Content/Imports/DelayedImportAddressTable.cs +++ b/src/Workshell.PE/Content/Imports/DelayedImportAddressTable.cs @@ -1,35 +1,38 @@ -#region License -// Copyright(c) Workshell Ltd -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Workshell.PE.Content -{ - public sealed class DelayedImportAddressTable : ImportAddressTableBase - { - internal DelayedImportAddressTable(PortableExecutableImage image, uint rva, ulong[] entries, ImportDirectoryEntryBase directoryEntry) : base(image, rva, entries, directoryEntry, true) - { - } - } -} +#region License +// Copyright(c) Workshell Ltd +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Workshell.PE.Extensions; + +namespace Workshell.PE.Content +{ + public sealed class DelayedImportAddressTable : ImportAddressTableBase + { + internal DelayedImportAddressTable(PortableExecutableImage image, uint rva, ulong[] entries, ImportDirectoryEntryBase directoryEntry) : base(image, rva, entries, directoryEntry, true) + { + } + } +} diff --git a/src/Workshell.PE/Content/Imports/DelayedImportAddressTables.cs b/src/Workshell.PE/Content/Imports/DelayedImportAddressTables.cs index 93a32cb..0d7efe6 100644 --- a/src/Workshell.PE/Content/Imports/DelayedImportAddressTables.cs +++ b/src/Workshell.PE/Content/Imports/DelayedImportAddressTables.cs @@ -38,17 +38,47 @@ internal DelayedImportAddressTables(PortableExecutableImage image, DataDirectory #region Static Methods - public static async Task GetLookupTableAsync(PortableExecutableImage image, DelayedImportDirectory directory = null) + public static DelayedImportAddressTables GetLookupTables(PortableExecutableImage image, DelayedImportDirectory directory = null) { - return await GetTableAsync(image, directory, (entry) => entry.DelayNameTable).ConfigureAwait(false); + return GetLookupTablesAsync(image, directory).GetAwaiter().GetResult(); } - public static async Task GetAddressTableAsync(PortableExecutableImage image, DelayedImportDirectory directory = null) + public static DelayedImportAddressTables GetAddressTables(PortableExecutableImage image, DelayedImportDirectory directory = null) { - return await GetTableAsync(image, directory, (entry) => entry.DelayAddressTable).ConfigureAwait(false); + return GetAddressTablesAsync(image, directory).GetAwaiter().GetResult(); } - private static async Task GetTableAsync(PortableExecutableImage image, DelayedImportDirectory directory, Func thunkHandler) + public static DelayedImportAddressTables GetBoundAddressTables(PortableExecutableImage image, DelayedImportDirectory directory = null) + { + return GetBoundAddressTablesAsync(image, directory).GetAwaiter().GetResult(); + } + + public static DelayedImportAddressTables GetUnloadAddressTables(PortableExecutableImage image, DelayedImportDirectory directory = null) + { + return GetUnloadAddressTablesAsync(image, directory).GetAwaiter().GetResult(); + } + + public static async Task GetLookupTablesAsync(PortableExecutableImage image, DelayedImportDirectory directory = null) + { + return await GetTablesAsync(image, directory, (entry) => entry.DelayNameTable).ConfigureAwait(false); + } + + public static async Task GetAddressTablesAsync(PortableExecutableImage image, DelayedImportDirectory directory = null) + { + return await GetTablesAsync(image, directory, (entry) => entry.DelayAddressTable).ConfigureAwait(false); + } + + public static async Task GetBoundAddressTablesAsync(PortableExecutableImage image, DelayedImportDirectory directory = null) + { + return await GetTablesAsync(image, directory, (entry) => entry.BoundDelayAddressTable).ConfigureAwait(false); + } + + public static async Task GetUnloadAddressTablesAsync(PortableExecutableImage image, DelayedImportDirectory directory = null) + { + return await GetTablesAsync(image, directory, (entry) => entry.UnloadDelayAddressTable).ConfigureAwait(false); + } + + private static async Task GetTablesAsync(PortableExecutableImage image, DelayedImportDirectory directory, Func thunkHandler) { if (directory == null) directory = await DelayedImportDirectory.GetAsync(image).ConfigureAwait(false); diff --git a/src/Workshell.PE/Content/Imports/DelayedImportDirectoryEntry.cs b/src/Workshell.PE/Content/Imports/DelayedImportDirectoryEntry.cs index 9b13270..7b56ea4 100644 --- a/src/Workshell.PE/Content/Imports/DelayedImportDirectoryEntry.cs +++ b/src/Workshell.PE/Content/Imports/DelayedImportDirectoryEntry.cs @@ -41,8 +41,8 @@ internal DelayedImportDirectoryEntry(PortableExecutableImage image, Location loc ModuleHandle = descriptor.ModuleHandle; DelayAddressTable = descriptor.DelayAddressTable; DelayNameTable = descriptor.DelayNameTable; - BoundDelayIAT = descriptor.BoundDelayIAT; - UnloadDelayIAT = descriptor.UnloadDelayIAT; + BoundDelayAddressTable = descriptor.BoundDelayIAT; + UnloadDelayAddressTable = descriptor.UnloadDelayIAT; TimeDateStamp = descriptor.TimeDateStamp; } @@ -78,10 +78,10 @@ public string GetName() public uint DelayNameTable { get; } [FieldAnnotation("Bound Delay Import Address Table", Order = 6)] - public uint BoundDelayIAT { get; } + public uint BoundDelayAddressTable { get; } [FieldAnnotation("Unload Delay Import Address Table", Order = 7)] - public uint UnloadDelayIAT { get; } + public uint UnloadDelayAddressTable { get; } [FieldAnnotation("Date/Time Stamp", Order = 8)] public uint TimeDateStamp { get; } diff --git a/src/Workshell.PE/Content/Imports/DelayedImportHintNameTable.cs b/src/Workshell.PE/Content/Imports/DelayedImportHintNameTable.cs index 5a3200e..4886f1c 100644 --- a/src/Workshell.PE/Content/Imports/DelayedImportHintNameTable.cs +++ b/src/Workshell.PE/Content/Imports/DelayedImportHintNameTable.cs @@ -38,13 +38,20 @@ internal DelayedImportHintNameTable(PortableExecutableImage image, DataDirectory #region Static Methods + public static DelayedImportHintNameTable Get(PortableExecutableImage image, DelayedImportDirectory directory = null) + { + return GetAsync(image, directory).GetAwaiter().GetResult(); + } + public static async Task GetAsync(PortableExecutableImage image, DelayedImportDirectory directory = null) { if (directory == null) + { directory = await DelayedImportDirectory.GetAsync(image).ConfigureAwait(false); + } var entries = new Dictionary>(); - var ilt = await DelayedImportAddressTables.GetLookupTableAsync(image, directory).ConfigureAwait(false); + var ilt = await DelayedImportAddressTables.GetLookupTablesAsync(image, directory).ConfigureAwait(false); var calc = image.GetCalculator(); var stream = image.GetStream(); diff --git a/src/Workshell.PE/Content/Imports/DelayedImports.cs b/src/Workshell.PE/Content/Imports/DelayedImports.cs index dbb41eb..70127b5 100644 --- a/src/Workshell.PE/Content/Imports/DelayedImports.cs +++ b/src/Workshell.PE/Content/Imports/DelayedImports.cs @@ -58,7 +58,7 @@ public static async Task GetAsync(PortableExecutableImage image) return null; } - var ilt = await DelayedImportAddressTables.GetLookupTableAsync(image, directory).ConfigureAwait(false); + var ilt = await DelayedImportAddressTables.GetLookupTablesAsync(image, directory).ConfigureAwait(false); if (ilt == null) { diff --git a/src/Workshell.PE/Content/Imports/ImportDirectoryEntryBase.cs b/src/Workshell.PE/Content/Imports/ImportDirectoryEntryBase.cs index 5a8e36b..63cc5ca 100644 --- a/src/Workshell.PE/Content/Imports/ImportDirectoryEntryBase.cs +++ b/src/Workshell.PE/Content/Imports/ImportDirectoryEntryBase.cs @@ -1,69 +1,99 @@ -#region License -// Copyright(c) Workshell Ltd -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; -using Workshell.PE.Extensions; - -namespace Workshell.PE.Content -{ - - public abstract class ImportDirectoryEntryBase : ISupportsLocation, ISupportsBytes - { - private readonly PortableExecutableImage _image; - - protected internal ImportDirectoryEntryBase(PortableExecutableImage image, Location location, bool isDelayed) - { - _image = image; - - Location = location; - IsDelayed = isDelayed; - } - - #region Methods - - public byte[] GetBytes() - { - return GetBytesAsync().GetAwaiter().GetResult(); - } - - public async Task GetBytesAsync() - { - var stream = _image.GetStream(); - var buffer = await stream.ReadBytesAsync(Location).ConfigureAwait(false); - - return buffer; - } - - #endregion - - #region Properties - - public Location Location { get; } - public bool IsDelayed { get; } - public abstract uint Name { get; } - - #endregion - } -} +#region License +// Copyright(c) Workshell Ltd +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Workshell.PE.Extensions; + +namespace Workshell.PE.Content +{ + + public abstract class ImportDirectoryEntryBase : ISupportsLocation, ISupportsBytes + { + private readonly PortableExecutableImage _image; + + protected internal ImportDirectoryEntryBase(PortableExecutableImage image, Location location, bool isDelayed) + { + _image = image; + + Location = location; + IsDelayed = isDelayed; + } + + #region Methods + + public byte[] GetBytes() + { + return GetBytesAsync().GetAwaiter().GetResult(); + } + + public async Task GetBytesAsync() + { + var stream = _image.GetStream(); + var buffer = await stream.ReadBytesAsync(Location).ConfigureAwait(false); + + return buffer; + } + + public string GetName() + { + return GetNameAsync().GetAwaiter().GetResult(); + } + + public async Task GetNameAsync() + { + var stream = _image.GetStream(); + var calc = _image.GetCalculator(); + var builder = new StringBuilder(256); + var offset = calc.RVAToOffset(Name); + + stream.Seek(offset, SeekOrigin.Begin); + + while (true) + { + var b = await stream.ReadByteAsync().ConfigureAwait(false); + + if (b <= 0) + { + break; + } + + builder.Append((char)b); + } + + return builder.ToString(); + } + + #endregion + + #region Properties + + public Location Location { get; } + public bool IsDelayed { get; } + public abstract uint Name { get; } + + #endregion + } +} diff --git a/src/Workshell.PE/Content/Imports/ImportLibraryBase.cs b/src/Workshell.PE/Content/Imports/ImportLibraryBase.cs index aeb43a7..ee86127 100644 --- a/src/Workshell.PE/Content/Imports/ImportLibraryBase.cs +++ b/src/Workshell.PE/Content/Imports/ImportLibraryBase.cs @@ -1,90 +1,90 @@ -#region License -// Copyright(c) Workshell Ltd -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Workshell.PE.Content -{ - public abstract class ImportLibraryBase : IEnumerable - { - private readonly ImportLibraryFunction[] _functions; - - protected internal ImportLibraryBase(ImportLibraryFunction[] functions, string name, bool isDelayed) - { - _functions = functions; - - Name = name; - Count = _functions.Length; - IsDelayed = isDelayed; - } - - #region Methods - - public IEnumerator GetEnumerator() - { - foreach (var function in _functions) - yield return function; - } - - System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - public override string ToString() - { - return $"Name: {Name}, Imported Function Count: {_functions.Length}"; - } - - public IEnumerable GetNamedFunctions() - { - foreach (var function in _functions) - { - if (function.BindingType == ImportLibraryBindingType.Name) - yield return (ImportLibraryNamedFunction)function; - } - } - - public IEnumerable GetOrdinalFunctions() - { - foreach (var function in _functions) - { - if (function.BindingType == ImportLibraryBindingType.Ordinal) - yield return (ImportLibraryOrdinalFunction)function; - } - } - - #endregion - - #region Properties - - public ImportLibraryFunction this[int index] => _functions[index]; - public string Name { get; } - public int Count { get; } - public bool IsDelayed { get; } - - #endregion - } -} +#region License +// Copyright(c) Workshell Ltd +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Workshell.PE.Content +{ + public abstract class ImportLibraryBase : IEnumerable + { + private readonly ImportLibraryFunction[] _functions; + + protected internal ImportLibraryBase(ImportLibraryFunction[] functions, string name, bool isDelayed) + { + _functions = functions; + + Name = name; + Count = _functions.Length; + IsDelayed = isDelayed; + } + + #region Methods + + public IEnumerator GetEnumerator() + { + foreach (var function in _functions) + yield return function; + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override string ToString() + { + return $"Name: {Name}, Imported Function Count: {_functions.Length}"; + } + + public IEnumerable GetNamedFunctions() + { + foreach (var function in _functions) + { + if (function.BindingType == ImportLibraryBindingType.Name) + yield return (ImportLibraryNamedFunction)function; + } + } + + public IEnumerable GetOrdinalFunctions() + { + foreach (var function in _functions) + { + if (function.BindingType == ImportLibraryBindingType.Ordinal) + yield return (ImportLibraryOrdinalFunction)function; + } + } + + #endregion + + #region Properties + + public ImportLibraryFunction this[int index] => _functions[index]; + public string Name { get; } + public int Count { get; } + public bool IsDelayed { get; } + + #endregion + } +} diff --git a/src/Workshell.PE/Content/Imports/ImportLibraryFunction.cs b/src/Workshell.PE/Content/Imports/ImportLibraryFunction.cs index 6b6bee6..081f870 100644 --- a/src/Workshell.PE/Content/Imports/ImportLibraryFunction.cs +++ b/src/Workshell.PE/Content/Imports/ImportLibraryFunction.cs @@ -1,98 +1,98 @@ -#region License -// Copyright(c) Workshell Ltd -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -using System; -using System.Collections.Generic; -using System.Text; - -namespace Workshell.PE.Content -{ - public enum ImportLibraryBindingType - { - Name, - Ordinal - } - - public abstract class ImportLibraryFunction - { - internal ImportLibraryFunction(ImportAddressTableEntryBase tableEntry) - { - TableEntry = tableEntry; - } - - #region Properties - - public ImportAddressTableEntryBase TableEntry { get; } - public abstract ImportLibraryBindingType BindingType { get; } - - #endregion - } - - public sealed class ImportLibraryOrdinalFunction : ImportLibraryFunction - { - internal ImportLibraryOrdinalFunction(ImportAddressTableEntryBase tableEntry, int ordinal) : base(tableEntry) - { - Ordinal = ordinal; - } - - #region Methods - - public override string ToString() - { - return $"{Ordinal:D4} (0x{Ordinal:X4})"; - } - - #endregion - - #region Properties - - public override ImportLibraryBindingType BindingType => ImportLibraryBindingType.Ordinal; - public int Ordinal { get; } - - #endregion - } - - public sealed class ImportLibraryNamedFunction : ImportLibraryFunction - { - internal ImportLibraryNamedFunction(ImportAddressTableEntryBase tableEntry, ImportHintNameEntryBase hintEntry) : base(tableEntry) - { - HintEntry = hintEntry; - } - - #region Methods - - public override string ToString() - { - return HintEntry.ToString(); - } - - #endregion - - #region Properties - - public override ImportLibraryBindingType BindingType => ImportLibraryBindingType.Name; - public ImportHintNameEntryBase HintEntry { get; } - public string Name => HintEntry.Name; - - #endregion - } -} +#region License +// Copyright(c) Workshell Ltd +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Workshell.PE.Content +{ + public enum ImportLibraryBindingType + { + Name, + Ordinal + } + + public abstract class ImportLibraryFunction + { + internal ImportLibraryFunction(ImportAddressTableEntryBase tableEntry) + { + TableEntry = tableEntry; + } + + #region Properties + + public ImportAddressTableEntryBase TableEntry { get; } + public abstract ImportLibraryBindingType BindingType { get; } + + #endregion + } + + public sealed class ImportLibraryOrdinalFunction : ImportLibraryFunction + { + internal ImportLibraryOrdinalFunction(ImportAddressTableEntryBase tableEntry, int ordinal) : base(tableEntry) + { + Ordinal = ordinal; + } + + #region Methods + + public override string ToString() + { + return $"{Ordinal:D4} (0x{Ordinal:X4})"; + } + + #endregion + + #region Properties + + public override ImportLibraryBindingType BindingType => ImportLibraryBindingType.Ordinal; + public int Ordinal { get; } + + #endregion + } + + public sealed class ImportLibraryNamedFunction : ImportLibraryFunction + { + internal ImportLibraryNamedFunction(ImportAddressTableEntryBase tableEntry, ImportHintNameEntryBase hintEntry) : base(tableEntry) + { + HintEntry = hintEntry; + } + + #region Methods + + public override string ToString() + { + return HintEntry.ToString(); + } + + #endregion + + #region Properties + + public override ImportLibraryBindingType BindingType => ImportLibraryBindingType.Name; + public ImportHintNameEntryBase HintEntry { get; } + public string Name => HintEntry.Name; + + #endregion + } +} diff --git a/src/Workshell.PE/Content/Imports/ImportsBase.cs b/src/Workshell.PE/Content/Imports/ImportsBase.cs index da7ef14..e4afc67 100644 --- a/src/Workshell.PE/Content/Imports/ImportsBase.cs +++ b/src/Workshell.PE/Content/Imports/ImportsBase.cs @@ -1,66 +1,66 @@ -#region License -// Copyright(c) Workshell Ltd -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; - -namespace Workshell.PE.Content -{ - public abstract class ImportsBase : IEnumerable - where TLibrary : ImportLibraryBase - { - private readonly TLibrary[] _libraries; - - protected internal ImportsBase(TLibrary[] libraries) - { - _libraries = libraries; - - Count = _libraries.Length; - } - - #region Methods - - public IEnumerator GetEnumerator() - { - foreach (var library in _libraries) - yield return library; - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - - #endregion - - #region Properties - - public int Count { get; } - public TLibrary this[int index] => _libraries[index]; - public TLibrary this[string name] => _libraries.FirstOrDefault(lib => string.Compare(name, lib.Name, StringComparison.OrdinalIgnoreCase) == 0); - - #endregion - } -} +#region License +// Copyright(c) Workshell Ltd +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Workshell.PE.Content +{ + public abstract class ImportsBase : IEnumerable + where TLibrary : ImportLibraryBase + { + private readonly TLibrary[] _libraries; + + protected internal ImportsBase(TLibrary[] libraries) + { + _libraries = libraries; + + Count = _libraries.Length; + } + + #region Methods + + public IEnumerator GetEnumerator() + { + foreach (var library in _libraries) + yield return library; + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + #endregion + + #region Properties + + public int Count { get; } + public TLibrary this[int index] => _libraries[index]; + public TLibrary this[string name] => _libraries.FirstOrDefault(lib => string.Compare(name, lib.Name, StringComparison.OrdinalIgnoreCase) == 0); + + #endregion + } +} diff --git a/src/Workshell.PE/Content/Relocation/RelocationTable.cs b/src/Workshell.PE/Content/Relocation/RelocationTable.cs index 620e731..b2a574b 100644 --- a/src/Workshell.PE/Content/Relocation/RelocationTable.cs +++ b/src/Workshell.PE/Content/Relocation/RelocationTable.cs @@ -52,12 +52,16 @@ public static RelocationTable Get(PortableExecutableImage image) public static async Task GetAsync(PortableExecutableImage image) { if (!image.NTHeaders.DataDirectories.Exists(DataDirectoryType.BaseRelocationTable)) + { return null; + } var dataDirectory = image.NTHeaders.DataDirectories[DataDirectoryType.BaseRelocationTable]; if (DataDirectory.IsNullOrEmpty(dataDirectory)) + { return null; + } var calc = image.GetCalculator(); var section = calc.RVAToSection(dataDirectory.VirtualAddress); @@ -109,7 +113,9 @@ public static async Task GetAsync(PortableExecutableImage image blocks.Add(block); if (blockSize >= dataDirectory.Size) + { break; + } blockOffset += sizeof(ulong); blockOffset += sizeof(ushort) * count; @@ -125,7 +131,9 @@ public static async Task GetAsync(PortableExecutableImage image public IEnumerator GetEnumerator() { foreach (var block in _blocks) + { yield return block; + } } IEnumerator IEnumerable.GetEnumerator() diff --git a/src/Workshell.PE/Content/Resources/Resource.cs b/src/Workshell.PE/Content/Resources/Resource.cs index 6c9044a..d5275cf 100644 --- a/src/Workshell.PE/Content/Resources/Resource.cs +++ b/src/Workshell.PE/Content/Resources/Resource.cs @@ -1,182 +1,182 @@ -#region License -// Copyright(c) Workshell Ltd -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. -#endregion - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Text; -using System.Threading.Tasks; - -namespace Workshell.PE.Resources -{ - public class Resource : ISupportsBytes - { - private readonly Dictionary _languages; - - protected Resource(PortableExecutableImage image, ResourceType type, ResourceDirectoryEntry entry, ResourceId id) - { - _languages = BuildLanguages(entry); - - Type = type; - Entry = entry; - Id = id; - Languages = _languages.Keys.OrderBy(k => k).ToArray(); - Image = image; - } - - #region Static Methods - - internal static async Task CreateAsync(PortableExecutableImage image, ResourceType type, ResourceDirectoryEntry entry, Type resourceType) - { - ResourceId id; - - if (entry.NameType == NameType.ID) - { - id = entry.GetId(); - } - else - { - id = await entry.GetNameAsync().ConfigureAwait(false); - } - - var ctors = resourceType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - var ctor = ctors.First(); - var resource = (Resource)ctor.Invoke(new object[] { image, type, entry, id }); - - return resource; - } - - #endregion - - #region Methods - - public override string ToString() - { - if (!Id.IsNumeric) - return Id.Name; - - return $"#{Id}"; - } - - public ResourceData GetData() - { - return GetData(ResourceLanguage.English.UnitedStates); - } - - public async Task GetDataAsync() - { - return await GetDataAsync(ResourceLanguage.English.UnitedStates).ConfigureAwait(false); - } - - public ResourceData GetData(ResourceLanguage language) - { - return GetDataAsync(language).GetAwaiter().GetResult(); - } - - public async Task GetDataAsync(ResourceLanguage language) - { - if (!_languages.ContainsKey(language)) - return null; - - var languageEntry = _languages[language]; - var entry = await languageEntry.GetDataEntryAsync().ConfigureAwait(false); - var data = entry.GetData(); - - return data; - } - - public byte[] GetBytes() - { - return GetBytes(ResourceLanguage.English.UnitedStates); - } - - public async Task GetBytesAsync() - { - return await GetBytesAsync(ResourceLanguage.English.UnitedStates); - } - - public byte[] GetBytes(ResourceLanguage language) - { - return GetBytesAsync(language).GetAwaiter().GetResult(); - } - - public async Task GetBytesAsync(ResourceLanguage language) - { - if (!_languages.ContainsKey(language)) - throw new PortableExecutableImageException(Image, $"Cannot find specified language: {language}"); - - var data = await GetDataAsync(language).ConfigureAwait(false); - - if (data == null) - throw new PortableExecutableImageException(Image, $"Cannot find resource data for language: {language}"); - - return await data.GetBytesAsync().ConfigureAwait(false); - } - - public void CopyTo(Stream stream) - { - CopyTo(stream, ResourceLanguage.English.UnitedStates); - } - - public async Task CopyToAsync(Stream stream) - { - await CopyToAsync(stream, ResourceLanguage.English.UnitedStates).ConfigureAwait(false); - } - - public void CopyTo(Stream stream, ResourceLanguage language) - { - CopyToAsync(stream, language).GetAwaiter().GetResult(); - } - - public async Task CopyToAsync(Stream stream, ResourceLanguage language) - { - var data = await GetDataAsync(language).ConfigureAwait(false); - - await data.CopyToAsync(stream).ConfigureAwait(false); - } - - private Dictionary BuildLanguages(ResourceDirectoryEntry parentEntry) - { - var results = new Dictionary(); - var directory = parentEntry.GetDirectory(); - - foreach (var entry in directory) - results.Add(entry.GetId(),entry); - - return results; - } - - #endregion - - #region Properties - - public ResourceType Type { get; } - public ResourceDirectoryEntry Entry { get; } - public ResourceId Id { get; } - public ResourceLanguage[] Languages { get; } - protected PortableExecutableImage Image { get; } - - #endregion - } -} +#region License +// Copyright(c) Workshell Ltd +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; + +namespace Workshell.PE.Resources +{ + public class Resource : ISupportsBytes + { + private readonly Dictionary _languages; + + protected Resource(PortableExecutableImage image, ResourceType type, ResourceDirectoryEntry entry, ResourceId id) + { + _languages = BuildLanguages(entry); + + Type = type; + Entry = entry; + Id = id; + Languages = _languages.Keys.OrderBy(res => res.LCID).ToArray(); + Image = image; + } + + #region Static Methods + + internal static async Task CreateAsync(PortableExecutableImage image, ResourceType type, ResourceDirectoryEntry entry, Type resourceType) + { + ResourceId id; + + if (entry.NameType == NameType.ID) + { + id = entry.GetId(); + } + else + { + id = await entry.GetNameAsync().ConfigureAwait(false); + } + + var ctors = resourceType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + var ctor = ctors.First(); + var resource = (Resource)ctor.Invoke(new object[] { image, type, entry, id }); + + return resource; + } + + #endregion + + #region Methods + + public override string ToString() + { + if (!Id.IsNumeric) + return Id.Name; + + return $"#{Id}"; + } + + public ResourceData GetData() + { + return GetData(ResourceLanguage.English.UnitedStates); + } + + public async Task GetDataAsync() + { + return await GetDataAsync(ResourceLanguage.English.UnitedStates).ConfigureAwait(false); + } + + public ResourceData GetData(ResourceLanguage language) + { + return GetDataAsync(language).GetAwaiter().GetResult(); + } + + public async Task GetDataAsync(ResourceLanguage language) + { + if (!_languages.ContainsKey(language)) + return null; + + var languageEntry = _languages[language]; + var entry = await languageEntry.GetDataEntryAsync().ConfigureAwait(false); + var data = entry.GetData(); + + return data; + } + + public byte[] GetBytes() + { + return GetBytes(ResourceLanguage.English.UnitedStates); + } + + public async Task GetBytesAsync() + { + return await GetBytesAsync(ResourceLanguage.English.UnitedStates); + } + + public byte[] GetBytes(ResourceLanguage language) + { + return GetBytesAsync(language).GetAwaiter().GetResult(); + } + + public async Task GetBytesAsync(ResourceLanguage language) + { + if (!_languages.ContainsKey(language)) + throw new PortableExecutableImageException(Image, $"Cannot find specified language: {language}"); + + var data = await GetDataAsync(language).ConfigureAwait(false); + + if (data == null) + throw new PortableExecutableImageException(Image, $"Cannot find resource data for language: {language}"); + + return await data.GetBytesAsync().ConfigureAwait(false); + } + + public void CopyTo(Stream stream) + { + CopyTo(stream, ResourceLanguage.English.UnitedStates); + } + + public async Task CopyToAsync(Stream stream) + { + await CopyToAsync(stream, ResourceLanguage.English.UnitedStates).ConfigureAwait(false); + } + + public void CopyTo(Stream stream, ResourceLanguage language) + { + CopyToAsync(stream, language).GetAwaiter().GetResult(); + } + + public async Task CopyToAsync(Stream stream, ResourceLanguage language) + { + var data = await GetDataAsync(language).ConfigureAwait(false); + + await data.CopyToAsync(stream).ConfigureAwait(false); + } + + private Dictionary BuildLanguages(ResourceDirectoryEntry parentEntry) + { + var results = new Dictionary(); + var directory = parentEntry.GetDirectory(); + + foreach (var entry in directory) + results.Add(entry.GetId(),entry); + + return results; + } + + #endregion + + #region Properties + + public ResourceType Type { get; } + public ResourceDirectoryEntry Entry { get; } + public ResourceId Id { get; } + public ResourceLanguage[] Languages { get; } + protected PortableExecutableImage Image { get; } + + #endregion + } +}