Last active
November 12, 2024 04:05
-
-
Save jbevain/7a976aaffff79366ac633fc3dfad3c40 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Reflection.Metadata; | |
using System.Reflection.PortableExecutable; | |
internal struct FileVersionReader | |
{ | |
private readonly PEReader _pe; | |
private BlobReader _reader; | |
public static Version? ReadFileVersion(PEReader pe) | |
{ | |
ArgumentNullException.ThrowIfNull(pe); | |
if (pe.PEHeaders?.PEHeader?.ResourceTableDirectory is { } rtd && rtd.RelativeVirtualAddress != 0) | |
{ | |
var reader = new FileVersionReader(pe, rtd.RelativeVirtualAddress); | |
return reader.ReadFileVersion(); | |
} | |
return null; | |
} | |
private FileVersionReader(PEReader pe, int rva) | |
{ | |
_pe = pe; | |
_reader = pe.GetSectionData(rva).GetReader(); | |
} | |
private Version? ReadFileVersion() | |
{ | |
const uint RT_VERSION = 16; | |
var directory = new IMAGE_RESOURCE_DIRECTORY(ref _reader); | |
for (uint i = 0; i < directory.NumberOfNamedEntries + directory.NumberOfIdEntries; i++) | |
{ | |
var entry = new IMAGE_RESOURCE_DIRECTORY_ENTRY(ref _reader); | |
if (entry.Name == RT_VERSION && (entry.OffsetToData & 0x80000000) != 0) | |
{ | |
_reader.Offset = checked((int)(entry.OffsetToData & ~0x80000000)); | |
return ReadID(); | |
} | |
} | |
return null; | |
} | |
private Version? ReadID() | |
{ | |
var directory = new IMAGE_RESOURCE_DIRECTORY(ref _reader); | |
for (uint i = 0; i < directory.NumberOfNamedEntries + directory.NumberOfIdEntries; i++) | |
{ | |
var entry = new IMAGE_RESOURCE_DIRECTORY_ENTRY(ref _reader); | |
if (entry.Name == 1 && (entry.OffsetToData & 0x80000000) != 0) | |
{ | |
_reader.Offset = checked((int)(entry.OffsetToData & ~0x80000000)); | |
return ReadLanguage(); | |
} | |
} | |
return null; | |
} | |
private Version? ReadLanguage() | |
{ | |
_ = new IMAGE_RESOURCE_DIRECTORY(ref _reader); | |
var entry = new IMAGE_RESOURCE_DIRECTORY_ENTRY(ref _reader); | |
_reader.Offset = checked((int)entry.OffsetToData); | |
return ReadVersionInfo(); | |
} | |
// "VS_VERSION_INFO\0" in LE Unicode, which is what is stored in the PE file | |
private static readonly byte[] VersionInfoKey = [86, 0, 83, 0, 95, 0, 86, 0, 69, 0, 82, 0, 83, 0, 73, 0, 79, 0, 78, 0, 95, 0, 73, 0, 78, 0, 70, 0, 79, 0, 0, 0]; | |
private Version? ReadVersionInfo() | |
{ | |
var entry = new IMAGE_RESOURCE_DATA_ENTRY(ref _reader); | |
var data = _pe.GetSectionData((int)entry.OffsetToData).GetReader(0, (int)entry.Size); | |
_ = data.ReadUInt16(); | |
_ = data.ReadUInt16(); | |
_ = data.ReadUInt16(); | |
for (int i = 0; i < VersionInfoKey.Length; i++) | |
{ | |
if (data.ReadByte() != VersionInfoKey[i]) | |
{ | |
return null; | |
} | |
} | |
_ = data.ReadUInt16(); | |
if (data.ReadUInt32() != 0xFEEF04BD) | |
{ | |
return null; | |
} | |
_ = data.ReadUInt32(); | |
var minor = data.ReadUInt16(); | |
var major = data.ReadUInt16(); | |
var revision = data.ReadUInt16(); | |
var build = data.ReadUInt16(); | |
return new(major, minor, build, revision); | |
} | |
private readonly struct IMAGE_RESOURCE_DIRECTORY | |
{ | |
public readonly uint Characteristics, TimeDateStamp; | |
public readonly ushort MajorVersion, MinorVersion, NumberOfNamedEntries, NumberOfIdEntries; | |
public IMAGE_RESOURCE_DIRECTORY(ref BlobReader blobReader) | |
{ | |
Characteristics = blobReader.ReadUInt32(); | |
TimeDateStamp = blobReader.ReadUInt32(); | |
MajorVersion = blobReader.ReadUInt16(); | |
MinorVersion = blobReader.ReadUInt16(); | |
NumberOfNamedEntries = blobReader.ReadUInt16(); | |
NumberOfIdEntries = blobReader.ReadUInt16(); | |
} | |
} | |
private readonly struct IMAGE_RESOURCE_DIRECTORY_ENTRY | |
{ | |
public readonly uint Name, OffsetToData; | |
public IMAGE_RESOURCE_DIRECTORY_ENTRY(ref BlobReader blobReader) | |
{ | |
Name = blobReader.ReadUInt32(); | |
OffsetToData = blobReader.ReadUInt32(); | |
} | |
} | |
private readonly struct IMAGE_RESOURCE_DATA_ENTRY | |
{ | |
public readonly uint OffsetToData, Size, CodePage, Reserved; | |
public IMAGE_RESOURCE_DATA_ENTRY(ref BlobReader blobReader) | |
{ | |
OffsetToData = blobReader.ReadUInt32(); | |
Size = blobReader.ReadUInt32(); | |
CodePage = blobReader.ReadUInt32(); | |
Reserved = blobReader.ReadUInt32(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment