diff --git a/src/AsmResolver.PE.Win32Resources/Icon/IconResource.cs b/src/AsmResolver.PE.Win32Resources/Icon/IconResource.cs index 9904b1a1a..0a97b0fe2 100644 --- a/src/AsmResolver.PE.Win32Resources/Icon/IconResource.cs +++ b/src/AsmResolver.PE.Win32Resources/Icon/IconResource.cs @@ -98,7 +98,7 @@ public void WriteToDirectory(IResourceDirectory rootDirectory) foreach (var (groupEntry, iconEntry) in entry.Value.GetIconEntries()) { newIconDirectory.Entries.Add(new ResourceDirectory(groupEntry.Id) - {Entries = {new ResourceData(0u, iconEntry)}}); + {Entries = {new ResourceData(1033, iconEntry)}}); } } diff --git a/src/AsmResolver.PE.Win32Resources/Version/StringTable.cs b/src/AsmResolver.PE.Win32Resources/Version/StringTable.cs index 3d88a0b1a..0db9a0e10 100644 --- a/src/AsmResolver.PE.Win32Resources/Version/StringTable.cs +++ b/src/AsmResolver.PE.Win32Resources/Version/StringTable.cs @@ -73,6 +73,53 @@ public class StringTable : VersionTableEntry, IEnumerable public const string SpecialBuildKey = "SpecialBuild"; + private readonly Dictionary _entries = new(); + + /// + /// Creates a new string table. + /// + /// The language identifier. + /// The code page. + public StringTable(ushort languageIdentifier, ushort codePage) + { + LanguageIdentifier = languageIdentifier; + CodePage = codePage; + } + + /// + public override string Key => $"{LanguageIdentifier:x4}{CodePage:x4}"; + + /// + /// Gets or sets the language identifier of this string table. + /// + public ushort LanguageIdentifier + { + get; + set; + } + + /// + /// Gets or sets the code page of this string table. + /// + public ushort CodePage + { + get; + set; + } + + /// + protected override VersionTableValueType ValueType => VersionTableValueType.Binary; + + /// + /// Gets or sets the value of a single field in the string table. + /// + /// The name of the field in the string table. + public string this[string key] + { + get => _entries[key]; + set => _entries[key] = value; + } + /// /// Reads a single StringTable structure from the provided input stream. /// @@ -122,53 +169,6 @@ private static KeyValuePair ReadEntry(ref BinaryStreamReader rea return new KeyValuePair(header.Key, value); } - private readonly Dictionary _entries = new Dictionary(); - - /// - /// Creates a new string table. - /// - /// The language identifier. - /// The code page. - public StringTable(ushort languageIdentifier, ushort codePage) - { - LanguageIdentifier = languageIdentifier; - CodePage = codePage; - } - - /// - public override string Key => $"{LanguageIdentifier:X4}{CodePage:X4}"; - - /// - /// Gets or sets the language identifier of this string table. - /// - public ushort LanguageIdentifier - { - get; - set; - } - - /// - /// Gets or sets the code page of this string table. - /// - public ushort CodePage - { - get; - set; - } - - /// - protected override VersionTableValueType ValueType => VersionTableValueType.Binary; - - /// - /// Gets or sets the value of a single field in the string table. - /// - /// The name of the field in the string table. - public string this[string key] - { - get => _entries[key]; - set => _entries[key] = value; - } - /// /// Adds (or overrides) a field to the string table. /// diff --git a/src/AsmResolver.PE.Win32Resources/Version/VersionInfoResource.cs b/src/AsmResolver.PE.Win32Resources/Version/VersionInfoResource.cs index 666591d7c..5839d2f07 100644 --- a/src/AsmResolver.PE.Win32Resources/Version/VersionInfoResource.cs +++ b/src/AsmResolver.PE.Win32Resources/Version/VersionInfoResource.cs @@ -15,6 +15,34 @@ public class VersionInfoResource : VersionTableEntry, IWin32Resource /// public const string VsVersionInfoKey = "VS_VERSION_INFO"; + private FixedVersionInfo _fixedVersionInfo = new(); + private readonly Dictionary _entries = new(); + + /// + public override string Key => VsVersionInfoKey; + + /// + protected override VersionTableValueType ValueType => VersionTableValueType.Binary; + + /// + /// Gets the fixed version info stored in this version resource. + /// + public FixedVersionInfo FixedVersionInfo + { + get => _fixedVersionInfo; + set => _fixedVersionInfo = value ?? throw new ArgumentNullException(nameof(value)); + } + + /// + /// Gets or sets a version table entry by its name. + /// + /// The name of the child. + public VersionTableEntry this[string name] + { + get => _entries[name]; + set => _entries[name] = value; + } + /// /// Obtains the version info resource from the provided root win32 resources directory. /// @@ -97,34 +125,6 @@ private static VersionTableEntry ReadNextEntry(ref BinaryStreamReader reader) }; } - private FixedVersionInfo _fixedVersionInfo = new FixedVersionInfo(); - private readonly Dictionary _entries = new Dictionary(); - - /// - public override string Key => VsVersionInfoKey; - - /// - protected override VersionTableValueType ValueType => VersionTableValueType.Binary; - - /// - /// Gets the fixed version info stored in this version resource. - /// - public FixedVersionInfo FixedVersionInfo - { - get => _fixedVersionInfo; - set => _fixedVersionInfo = value ?? throw new ArgumentNullException(nameof(value)); - } - - /// - /// Gets or sets a version table entry by its name. - /// - /// The name of the child. - public VersionTableEntry this[string name] - { - get => _entries[name]; - set => _entries[name] = value; - } - /// /// Gets a collection of entries stored in the version resource. /// @@ -181,9 +181,11 @@ public override uint GetPhysicalSize() protected override void WriteValue(IBinaryStreamWriter writer) { FixedVersionInfo.Write(writer); - writer.Align(4); foreach (var entry in _entries.Values) + { + writer.Align(4); entry.Write(writer); + } } /// @@ -196,7 +198,7 @@ public void WriteToDirectory(IResourceDirectory rootDirectory) { new ResourceDirectory(1) { - Entries = {new ResourceData(0u, this)} + Entries = {new ResourceData(1033, this)} } } }; diff --git a/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj b/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj index ff33ce493..ba9f10fa3 100644 --- a/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj +++ b/test/AsmResolver.PE.Win32Resources.Tests/AsmResolver.PE.Win32Resources.Tests.csproj @@ -23,6 +23,7 @@ + @@ -40,8 +41,4 @@ - - - - diff --git a/test/AsmResolver.PE.Win32Resources.Tests/Properties/Resources.Designer.cs b/test/AsmResolver.PE.Win32Resources.Tests/Properties/Resources.Designer.cs index 866341677..3c3cd2021 100644 --- a/test/AsmResolver.PE.Win32Resources.Tests/Properties/Resources.Designer.cs +++ b/test/AsmResolver.PE.Win32Resources.Tests/Properties/Resources.Designer.cs @@ -51,5 +51,12 @@ internal static byte[] HelloWorld { return ((byte[])(obj)); } } + + internal static byte[] HelloWorld_PaddedVersionInfo { + get { + object obj = ResourceManager.GetObject("HelloWorld_PaddedVersionInfo", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/test/AsmResolver.PE.Win32Resources.Tests/Properties/Resources.resx b/test/AsmResolver.PE.Win32Resources.Tests/Properties/Resources.resx index 4a49ff593..e6b342ebd 100644 --- a/test/AsmResolver.PE.Win32Resources.Tests/Properties/Resources.resx +++ b/test/AsmResolver.PE.Win32Resources.Tests/Properties/Resources.resx @@ -3,7 +3,7 @@ - + @@ -21,4 +21,7 @@ ..\Resources\HelloWorld.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - \ No newline at end of file + + ..\Resources\HelloWorld.PaddedVersionInfo.exe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/test/AsmResolver.PE.Win32Resources.Tests/Resources/HelloWorld.PaddedVersionInfo.exe b/test/AsmResolver.PE.Win32Resources.Tests/Resources/HelloWorld.PaddedVersionInfo.exe new file mode 100644 index 000000000..4391dfd46 Binary files /dev/null and b/test/AsmResolver.PE.Win32Resources.Tests/Resources/HelloWorld.PaddedVersionInfo.exe differ diff --git a/test/AsmResolver.PE.Win32Resources.Tests/Version/VersionInfoResourceTest.cs b/test/AsmResolver.PE.Win32Resources.Tests/Version/VersionInfoResourceTest.cs index a9699d22b..296f352fd 100644 --- a/test/AsmResolver.PE.Win32Resources.Tests/Version/VersionInfoResourceTest.cs +++ b/test/AsmResolver.PE.Win32Resources.Tests/Version/VersionInfoResourceTest.cs @@ -2,13 +2,24 @@ using System.IO; using AsmResolver.IO; using AsmResolver.PE.DotNet.Builder; +using AsmResolver.PE.File; +using AsmResolver.PE.File.Headers; +using AsmResolver.PE.Win32Resources.Builder; using AsmResolver.PE.Win32Resources.Version; +using AsmResolver.Tests.Runners; using Xunit; namespace AsmResolver.PE.Win32Resources.Tests.Version { - public class VersionInfoSegmentTest + public class VersionInfoResourceTest : IClassFixture { + private readonly TemporaryDirectoryFixture _fixture; + + public VersionInfoResourceTest(TemporaryDirectoryFixture fixture) + { + _fixture = fixture; + } + [Fact] public void ReadFixedVersion() { @@ -192,5 +203,41 @@ public void PersistentVersionResource() Assert.Equal(versionInfo.FixedVersionInfo.ProductVersion, newVersionInfo.FixedVersionInfo.ProductVersion); } + [Fact] + public void VersionInfoAlignment() + { + // https://github.com/Washi1337/AsmResolver/issues/202 + + // Open dummy + var file = PEFile.FromBytes(Properties.Resources.HelloWorld_PaddedVersionInfo); + var image = PEImage.FromFile(file); + + // Update version info. + var versionInfo = VersionInfoResource.FromDirectory(image.Resources!)!; + var info = versionInfo.GetChild(StringFileInfo.StringFileInfoKey); + info.Tables[0][StringTable.FileDescriptionKey] = "This is a test application"; + versionInfo.WriteToDirectory(image.Resources!); + + // Replace section. + var resourceBuffer = new ResourceDirectoryBuffer(); + resourceBuffer.AddDirectory(image.Resources!); + + var section = file.GetSectionContainingRva(file.OptionalHeader.GetDataDirectory(DataDirectoryIndex.ResourceDirectory).VirtualAddress); + section.Contents = resourceBuffer; + + file.UpdateHeaders(); + file.OptionalHeader.SetDataDirectory(DataDirectoryIndex.ResourceDirectory, + new DataDirectory(resourceBuffer.Rva, resourceBuffer.GetPhysicalSize())); + + // Rebuild + using var stream = new MemoryStream(); + file.Write(stream); + + // Reopen and verify. + var newImage = PEImage.FromBytes(stream.ToArray()); + var newVersionInfo = VersionInfoResource.FromDirectory(newImage.Resources!)!; + var newInfo = newVersionInfo.GetChild(StringFileInfo.StringFileInfoKey); + Assert.Equal("This is a test application", newInfo.Tables[0][StringTable.FileDescriptionKey]); + } } }