Decode RT_VERSION resource from PE executables.

This commit is contained in:
2018-03-09 16:21:19 +00:00
parent e18d4c26c3
commit 658a82ac10
7 changed files with 520 additions and 1 deletions

View File

@@ -179,6 +179,58 @@ namespace exeinfo
{
recognized = true;
Console.Write(peExe.Information);
if(((PE)peExe).Versions != null)
foreach(PE.Version vers in ((PE)peExe).Versions)
{
Console.WriteLine("\tVersion resource {0}:", vers.Name);
Console.WriteLine("\t\tFile version: {0}", vers.FileVersion);
Console.WriteLine("\t\tProduct version: {0}", vers.ProductVersion);
Console.WriteLine("\t\tFile type: {0}", PE.Version.TypeToString(vers.FileType));
if(vers.FileType == PE.VersionFileType.VFT_DRV)
Console.WriteLine("\t\tFile subtype: {0} driver",
PE.Version.DriverToString(vers.FileSubtype));
else if(vers.FileType == PE.VersionFileType.VFT_DRV)
Console.WriteLine("\t\tFile subtype: {0} font", PE.Version.FontToString(vers.FileSubtype));
else if(vers.FileSubtype > 0)
Console.WriteLine("\t\tFile subtype: {0}", (uint)vers.FileSubtype);
Console.WriteLine("\t\tFile flags: {0}", vers.FileFlags);
Console.WriteLine("\t\tFile OS: {0}", PE.Version.OsToString(vers.FileOS));
foreach(KeyValuePair<string, Dictionary<string, string>> strByLang in vers.StringsByLanguage)
{
string cultureName;
string encodingName;
try
{
cultureName = new CultureInfo(Convert.ToInt32(strByLang.Key.Substring(0, 4), 16))
.DisplayName;
}
catch
{
cultureName =
$"unsupported culture 0x{Convert.ToInt32(strByLang.Key.Substring(0, 4), 16):X4}";
}
try
{
encodingName = Encoding
.GetEncoding(Convert.ToInt32(strByLang.Key.Substring(4), 16))
.EncodingName;
}
catch
{
encodingName =
$"unsupported encoding 0x{Convert.ToInt32(strByLang.Key.Substring(4), 16):X4}";
}
Console.WriteLine("\t\tStrings for {0} in codepage {1}:", cultureName, encodingName);
foreach(KeyValuePair<string, string> strings in strByLang.Value)
Console.WriteLine("\t\t\t{0}: {1}", strings.Key, strings.Value);
}
}
if(peExe.Strings != null && peExe.Strings.Any())
{

View File

@@ -34,5 +34,13 @@ namespace libexeinfo
const ushort SIGNATURE = 0x00004550;
const ushort PE32 = COFF.ZMAGIC;
internal const ushort PE32Plus = 0x20b;
/// <summary>
/// Signature for a <see cref="FixedFileInfo" />
/// </summary>
const string FIXED_FILE_INFO_SIG = "VS_VERSION_INFO";
/// <summary>
/// Signature for list of name=value strings inside a version resource
/// </summary>
const string STRING_FILE_INFO = "StringFileInfo";
}
}

View File

@@ -176,5 +176,92 @@ namespace libexeinfo
/// </summary>
IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION = 16
}
}
/// <summary>
/// Version file flags.
/// </summary>
[Flags]
public enum VersionFileFlags : uint
{
VS_FF_DEBUG = 0x00000001,
VS_FF_INFOINFERRED = 0x00000010,
VS_FF_PATCHED = 0x00000004,
VS_FF_PRERELEASE = 0x00000002,
VS_FF_PRIVATEBUILD = 0x00000008,
VS_FF_SPECIALBUILD = 0x00000020
}
/// <summary>
/// Version file operating system.
/// </summary>
public enum VersionFileOS : uint
{
VOS_DOS = 0x00010000,
VOS_NT = 0x00040000,
VOS_WINDOWS16 = 0x00000001,
VOS_WINDOWS32 = 0x00000004,
VOS_OS216 = 0x00020000,
VOS_OS232 = 0x00030000,
VOS_PM16 = 0x00000002,
VOS_PM32 = 0x00000003,
VOS_UNKNOWN = 0x00000000,
// Combinations, some have no sense
VOS_DOS_NT = 0x00050000,
VOS_DOS_WINDOWS16 = 0x00010001,
VOS_DOS_WINDOWS32 = 0x00010004,
VOS_DOS_PM16 = 0x00010002,
VOS_DOS_PM32 = 0x00010003,
VOS_NT_WINDOWS16 = 0x00040001,
VOS_NT_WINDOWS32 = 0x00040004,
VOS_NT_PM16 = 0x00040002,
VOS_NT_PM32 = 0x00040003,
VOS_OS216_WINDOWS16 = 0x00020001,
VOS_OS216_WINDOWS32 = 0x00020004,
VOS_OS216_PM16 = 0x00020002,
VOS_OS216_PM32 = 0x00020003,
VOS_OS232_WINDOWS16 = 0x00030001,
VOS_OS232_WINDOWS32 = 0x00030004,
VOS_OS232_PM16 = 0x00030002,
VOS_OS232_PM32 = 0x00030003
}
/// <summary>
/// Version file subtype.
/// </summary>
public enum VersionFileSubtype : uint
{
VFT2_UNKNOWN = 0x00000000,
// Drivers
VFT2_DRV_COMM = 0x0000000A,
VFT2_DRV_DISPLAY = 0x00000004,
VFT2_DRV_INSTALLABLE = 0x00000008,
VFT2_DRV_KEYBOARD = 0x00000002,
VFT2_DRV_LANGUAGE = 0x00000003,
VFT2_DRV_MOUSE = 0x00000005,
VFT2_DRV_NETWORK = 0x00000006,
VFT2_DRV_PRINTER = 0x00000001,
VFT2_DRV_SOUND = 0x00000009,
VFT2_DRV_SYSTEM = 0x00000007,
VFT2_DRV_VERSIONED_PRINTER = 0x0000000C,
// Fonts
VFT2_FONT_RASTER = 0x00000001,
VFT2_FONT_TRUETYPE = 0x00000003,
VFT2_FONT_VECTOR = 0x00000002
}
/// <summary>
/// Version file type.
/// </summary>
public enum VersionFileType : uint
{
VFT_APP = 0x00000001,
VFT_DLL = 0x00000002,
VFT_DRV = 0x00000003,
VFT_FONT = 0x00000004,
VFT_STATIC_LIB = 0x00000007,
VFT_UNKNOWN = 0x00000000,
VFT_VXD = 0x00000005
}
}
}

View File

@@ -53,6 +53,7 @@ namespace libexeinfo
COFF.SectionHeader[] sectionHeaders;
public ResourceNode WindowsResourcesRoot;
WindowsHeader64 winHeader;
public Version[] Versions;
/// <summary>
/// Initializes a new instance of the <see cref="T:libexeinfo.PE" /> class.
@@ -387,9 +388,14 @@ namespace libexeinfo
// TODO: Decode BeOS resource format
}
else
{
WindowsResourcesRoot = GetResourceNode(BaseStream, rsrc.pointerToRawData,
rsrc.virtualAddress,
rsrc.pointerToRawData, 0, null, 0);
Versions = GetVersions().ToArray();
strings.AddRange(from v in Versions from s in v.StringsByLanguage from k in s.Value select k.Value);
}
sectionHeaders = newSectionHeaders.Values.OrderBy(s => s.pointerToRawData).ToArray();
Segment[] segments = new Segment[sectionHeaders.Length];

View File

@@ -459,5 +459,39 @@ namespace libexeinfo
public byte[] data;
public int level;
}
/// <summary>
/// Node in a version resource
/// </summary>
class VersionNode
{
public ushort wValueLength;
public ushort wLength;
public ushort wType;
public VersionNode[] children;
public byte[] rgbData;
public string szName;
}
/// <summary>
/// Fixed file version info
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct FixedFileInfo
{
public uint dwSignature;
public uint dwStrucVersion;
public uint dwFileVersionMS;
public uint dwFileVersionLS;
public uint dwProductVersionMS;
public uint dwProductVersionLS;
public uint dwFileFlagsMask;
public uint dwFileFlags;
public uint dwFileOS;
public uint dwFileType;
public uint dwFileSubtype;
public uint dwFileDateMS;
public uint dwFileDateLS;
}
}
}

331
libexeinfo/PE/Version.cs Normal file
View File

@@ -0,0 +1,331 @@
//
// Version.cs
//
// Author:
// Natalia Portillo <claunia@claunia.com>
//
// Copyright (c) 2017-2018 Copyright © Claunia.com
//
// 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.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using libexeinfo.Windows;
namespace libexeinfo
{
public partial class PE
{
/// <summary>
/// Gets all the version resources from this instance
/// </summary>
/// <returns>The decoded version resources.</returns>
public List<Version> GetVersions()
{
return (from node in WindowsResourcesRoot.children
where node.id == (uint)ResourceTypes.RT_VERSION
from ids in node.children
from lang in ids.children
select new Version(lang.data, lang.name)).ToList();
}
/// <summary>
/// Represents a version ("RT_VERSION") resource
/// </summary>
public class Version
{
/// <summary>
/// Initializes a new instance of the <see cref="T:libexeinfo.NE.Version" /> class.
/// </summary>
/// <param name="data">Resource data.</param>
/// <param name="resourceName">Resource name.</param>
public Version(byte[] data, string resourceName = null)
{
if(data == null || data.Length < 5) return;
Name = resourceName;
StringsByLanguage = new Dictionary<string, Dictionary<string, string>>();
VersionNode root = GetNode(data, 0, out int rootLength);
DecodeNode(root, null, null);
}
/// <summary>
/// This contains a list of all name=value strings pairs sorted by language
/// </summary>
/// <value>List of all name=value strings pairs sorted by language.</value>
public Dictionary<string, Dictionary<string, string>> StringsByLanguage { get; }
/// <summary>
/// File version.
/// </summary>
/// <value>The file version.</value>
public string FileVersion { get; set; }
/// <summary>
/// Product version.
/// </summary>
/// <value>The product version.</value>
public string ProductVersion { get; set; }
/// <summary>
/// File flags.
/// </summary>
/// <value>The file flags.</value>
public VersionFileFlags FileFlags { get; set; }
/// <summary>
/// File operating system.
/// </summary>
/// <value>The file operating system.</value>
public VersionFileOS FileOS { get; set; }
/// <summary>
/// File type.
/// </summary>
/// <value>The type of the file.</value>
public VersionFileType FileType { get; set; }
/// <summary>
/// File subtype.
/// </summary>
/// <value>The file subtype.</value>
public VersionFileSubtype FileSubtype { get; set; }
/// <summary>
/// File date.
/// </summary>
/// <value>The file date.</value>
public DateTime FileDate { get; set; }
/// <summary>
/// Resource name
/// </summary>
/// <value>The resource name.</value>
public string Name { get; }
static VersionNode GetNode(byte[] data, int startPosition, out int nodeLength)
{
nodeLength = 0;
VersionNode node = new VersionNode
{
wLength = BitConverter.ToUInt16(data, startPosition + nodeLength),
wValueLength = BitConverter.ToUInt16(data, startPosition + nodeLength + 2),
wType = BitConverter.ToUInt16(data, startPosition + nodeLength + 4)
};
nodeLength += 6;
MemoryStream nameMs = new MemoryStream();
while(true)
{
if(data[startPosition + nodeLength] == 0 && data[startPosition + nodeLength + 1] == 0)
break;
nameMs.WriteByte(data[startPosition + nodeLength]);
nameMs.WriteByte(data[startPosition + nodeLength + 1]);
nodeLength+=2;
}
node.szName = Encoding.Unicode.GetString(nameMs.ToArray());
nodeLength+=2;
if(nodeLength % 4 > 0) nodeLength += 4 - nodeLength % 4;
int factor = node.wType == 1 ? 2 : 1;
node.rgbData = new byte[node.wValueLength * factor];
Array.Copy(data, startPosition + nodeLength, node.rgbData, 0, node.rgbData.Length);
nodeLength += node.rgbData.Length;
if(nodeLength % 4 > 0) nodeLength += 4 - nodeLength % 4;
string foo = Encoding.Unicode.GetString(node.rgbData);
List<VersionNode> children = new List<VersionNode>();
while(nodeLength < node.wLength)
{
children.Add(GetNode(data, startPosition + nodeLength, out int childLength));
nodeLength += childLength;
}
if(children.Count > 0) node.children = children.ToArray();
return node;
}
void DecodeNode(VersionNode node, string parent, string grandparent)
{
if(node.szName == FIXED_FILE_INFO_SIG)
{
IntPtr infoPtr = Marshal.AllocHGlobal(node.wValueLength);
Marshal.Copy(node.rgbData, 0, infoPtr, node.wValueLength);
FixedFileInfo info = (FixedFileInfo)Marshal.PtrToStructure(infoPtr, typeof(FixedFileInfo));
Marshal.FreeHGlobal(infoPtr);
FileVersion =
$"{(info.dwFileVersionMS & 0xFFFF0000) >> 16}.{info.dwFileVersionMS & 0xFFFF:D2}.{(info.dwFileVersionLS & 0xFFFF0000) >> 16}.{info.dwFileVersionLS & 0xFFFF}";
ProductVersion =
$"{(info.dwProductVersionMS & 0xFFFF0000) >> 16}.{info.dwProductVersionMS & 0xFFFF:D2}.{(info.dwProductVersionLS & 0xFFFF0000) >> 16}.{info.dwProductVersionLS & 0xFFFF}";
FileFlags = (VersionFileFlags)(info.dwFileFlags & info.dwFileFlagsMask);
FileOS = (VersionFileOS)info.dwFileOS;
FileType = (VersionFileType)info.dwFileType;
FileSubtype = (VersionFileSubtype)info.dwFileSubtype;
FileDate = DateTime.FromFileTime(info.dwFileDateMS * 0x100000000 + info.dwFileDateLS);
}
if(parent == STRING_FILE_INFO)
{
Dictionary<string, string> strings = new Dictionary<string, string>();
StringsByLanguage.Add(node.szName, strings);
}
if(grandparent == STRING_FILE_INFO)
if(StringsByLanguage.TryGetValue(parent, out Dictionary<string, string> strings))
{
strings.Add(node.szName, Encoding.Unicode.GetString(node.rgbData));
}
if(node.children == null) return;
foreach(VersionNode n in node.children) DecodeNode(n, node.szName, parent);
}
/// <summary>
/// Converts a <see cref="VersionFileType" /> to string
/// </summary>
/// <returns>The string.</returns>
/// <param name="type">
/// <see cref="VersionFileType" />
/// </param>
public static string TypeToString(VersionFileType type)
{
switch(type)
{
case VersionFileType.VFT_APP: return "Application";
case VersionFileType.VFT_DLL: return "Dynamic-link library";
case VersionFileType.VFT_DRV: return "Device driver";
case VersionFileType.VFT_FONT: return "Font";
case VersionFileType.VFT_STATIC_LIB: return "Static-link library";
case VersionFileType.VFT_UNKNOWN: return "Unknown";
case VersionFileType.VFT_VXD: return "Virtual device";
default: return $"Unknown type code {(uint)type}";
}
}
/// <summary>
/// Converts a <see cref="VersionFileSubtype" /> to string, considering file type to be a driver
/// </summary>
/// <returns>The string.</returns>
/// <param name="subtype">
/// <see cref="VersionFileSubtype" />
/// </param>
public static string DriverToString(VersionFileSubtype subtype)
{
switch(subtype)
{
case VersionFileSubtype.VFT2_DRV_COMM: return "Communications";
case VersionFileSubtype.VFT2_DRV_DISPLAY: return "Display";
case VersionFileSubtype.VFT2_DRV_INSTALLABLE: return "Installable";
case VersionFileSubtype.VFT2_DRV_KEYBOARD: return "Keyboard";
case VersionFileSubtype.VFT2_DRV_LANGUAGE: return "Language";
case VersionFileSubtype.VFT2_DRV_MOUSE: return "Mouse";
case VersionFileSubtype.VFT2_DRV_NETWORK: return "Network";
case VersionFileSubtype.VFT2_DRV_PRINTER: return "Printer";
case VersionFileSubtype.VFT2_DRV_SOUND: return "Sound";
case VersionFileSubtype.VFT2_DRV_SYSTEM: return "System";
case VersionFileSubtype.VFT2_DRV_VERSIONED_PRINTER: return "Versioned";
case VersionFileSubtype.VFT2_UNKNOWN: return "Unknown";
default: return $"Unknown type code {(uint)subtype}";
}
}
/// <summary>
/// Converts a <see cref="VersionFileSubtype" /> to string, considering file type to be a font
/// </summary>
/// <returns>The string.</returns>
/// <param name="subtype">
/// <see cref="VersionFileSubtype" />
/// </param>
public static string FontToString(VersionFileSubtype subtype)
{
switch(subtype)
{
case VersionFileSubtype.VFT2_FONT_RASTER: return "Raster";
case VersionFileSubtype.VFT2_FONT_TRUETYPE: return "TrueType";
case VersionFileSubtype.VFT2_FONT_VECTOR: return "Vector";
case VersionFileSubtype.VFT2_UNKNOWN: return "Unknown";
default: return $"Unknown type code {(uint)subtype}";
}
}
/// <summary>
/// Converts a <see cref="VersionFileOS" /> to string
/// </summary>
/// <returns>The string.</returns>
/// <param name="os">
/// <see cref="VersionFileOS" />
/// </param>
public static string OsToString(VersionFileOS os)
{
switch(os)
{
case VersionFileOS.VOS_DOS: return "DOS";
case VersionFileOS.VOS_NT: return "Windows NT";
case VersionFileOS.VOS_WINDOWS16: return "16-bit Windows";
case VersionFileOS.VOS_WINDOWS32: return "32-bit Windows";
case VersionFileOS.VOS_OS216: return "16-bit OS/2";
case VersionFileOS.VOS_OS232: return "32-bit OS/2";
case VersionFileOS.VOS_PM16: return "16-bit Presentation Manager";
case VersionFileOS.VOS_PM32: return "32-bit Presentation Manager";
case VersionFileOS.VOS_UNKNOWN: return "Unknown";
case VersionFileOS.VOS_DOS_NT: return "DOS running under Windows NT";
case VersionFileOS.VOS_DOS_WINDOWS16: return "16-bit Windows running under DOS";
case VersionFileOS.VOS_DOS_WINDOWS32: return "32-bit Windows running under DOS";
case VersionFileOS.VOS_DOS_PM16: return "16-bit Presentation Manager running under DOS";
case VersionFileOS.VOS_DOS_PM32: return "32-bit Presentation Manager running under DOS";
case VersionFileOS.VOS_NT_WINDOWS16: return "16-bit Windows running under Windows NT";
case VersionFileOS.VOS_NT_WINDOWS32: return "32-bit Windows running under Windows NT";
case VersionFileOS.VOS_NT_PM16:
return "16-bit Presentation Manager running under Windows NT";
case VersionFileOS.VOS_NT_PM32:
return "32-bit Presentation Manager running under Windows NT";
case VersionFileOS.VOS_OS216_WINDOWS16: return "16-bit Windows running under 16-bit OS/2";
case VersionFileOS.VOS_OS216_WINDOWS32: return "32-bit Windows running under 16-bit OS/2";
case VersionFileOS.VOS_OS216_PM16:
return "16-bit Presentation Manager running under 16-bit OS/2";
case VersionFileOS.VOS_OS216_PM32:
return "32-bit Presentation Manager running under 16-bit OS/2";
case VersionFileOS.VOS_OS232_WINDOWS16: return "16-bit Windows running under 32-bit OS/2";
case VersionFileOS.VOS_OS232_WINDOWS32: return "32-bit Windows running under 32-bit OS/2";
case VersionFileOS.VOS_OS232_PM16:
return "16-bit Presentation Manager running under 32-bit OS/2";
case VersionFileOS.VOS_OS232_PM32:
return "32-bit Presentation Manager running under 32-bit OS/2";
default: return $"Unknown OS code {(uint)os}";
}
}
}
}
}

View File

@@ -67,6 +67,7 @@
<Compile Include="Os2\Enums.cs" />
<Compile Include="Os2\Resources.cs" />
<Compile Include="Os2\Structs.cs" />
<Compile Include="PE\Version.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MZ\Consts.cs" />
<Compile Include="MZ\Info.cs" />