diff options
| author | Karsten Heimrich <karsten.heimrich@qt.io> | 2024-11-20 18:03:04 +0100 |
|---|---|---|
| committer | Karsten Heimrich <karsten.heimrich@qt.io> | 2024-11-21 11:46:45 +0000 |
| commit | 71e14972fc4b2d96cc562afe86dc4feb7dba4cc6 (patch) | |
| tree | a14828f3ebb10c5a45383794e8482fd248ff6500 /QtVsTools.TestAdapter/Binary.cs | |
| parent | 2e3bd46aa19a60c863b38bc1a5f8a0c1ceb7fbc9 (diff) | |
Replace PEReader with native PE header parsing
Removed dependency on System.Reflection.Metadata and PEReader.
Implemented manual parsing of PE headers using native structures.
Fixes: QTVSADDINBUG-1258
Change-Id: I079c07ce0582740de236c301628c37c24e50a23e
Reviewed-by: Miguel Costa <miguel.costa@qt.io>
Diffstat (limited to 'QtVsTools.TestAdapter/Binary.cs')
| -rw-r--r-- | QtVsTools.TestAdapter/Binary.cs | 156 |
1 files changed, 150 insertions, 6 deletions
diff --git a/QtVsTools.TestAdapter/Binary.cs b/QtVsTools.TestAdapter/Binary.cs index cf0e8201..87747e29 100644 --- a/QtVsTools.TestAdapter/Binary.cs +++ b/QtVsTools.TestAdapter/Binary.cs @@ -5,7 +5,7 @@ using System; using System.IO; -using System.Reflection.PortableExecutable; +using System.Runtime.InteropServices; using Microsoft.VisualStudio.TestPlatform.ObjectModel.Logging; namespace QtVsTools.TestAdapter @@ -19,21 +19,152 @@ namespace QtVsTools.TestAdapter Gui } + [StructLayout(LayoutKind.Sequential)] + private struct IMAGE_DOS_HEADER + { + public ushort e_magic; + public ushort e_cblp; + public ushort e_cp; + public ushort e_crlc; + public ushort e_cparhdr; + public ushort e_minalloc; + public ushort e_maxalloc; + public ushort e_ss; + public ushort e_sp; + public ushort e_csum; + public ushort e_ip; + public ushort e_cs; + public ushort e_lfarlc; + public ushort e_ovno; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public ushort[] e_res1; + public ushort e_oemid; + public ushort e_oeminfo; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public ushort[] e_res2; + public int e_lfanew; // Offset to the NT header + } + + [StructLayout(LayoutKind.Sequential)] + private struct IMAGE_FILE_HEADER + { + public ushort Machine; + public ushort NumberOfSections; + public uint TimeDateStamp; + public uint PointerToSymbolTable; + public uint NumberOfSymbols; + public ushort SizeOfOptionalHeader; + public ushort Characteristics; + } + + [StructLayout(LayoutKind.Sequential)] + private struct IMAGE_OPTIONAL_HEADER + { + public ushort Magic; + public byte MajorLinkerVersion; + public byte MinorLinkerVersion; + public uint SizeOfCode; + public uint SizeOfInitializedData; + public uint SizeOfUninitializedData; + public uint AddressOfEntryPoint; + public uint BaseOfCode; + public ulong ImageBase; + public uint SectionAlignment; + public uint FileAlignment; + public ushort MajorOperatingSystemVersion; + public ushort MinorOperatingSystemVersion; + public ushort MajorImageVersion; + public ushort MinorImageVersion; + public ushort MajorSubsystemVersion; + public ushort MinorSubsystemVersion; + public uint Win32VersionValue; + public uint SizeOfImage; + public uint SizeOfHeaders; + public uint CheckSum; + public ushort Subsystem; + public ushort DllCharacteristics; + public ulong SizeOfStackReserve; + public ulong SizeOfStackCommit; + public ulong SizeOfHeapReserve; + public ulong SizeOfHeapCommit; + public uint LoaderFlags; + public uint NumberOfRvaAndSizes; + // IMAGE_DATA_DIRECTORY is variable-length; exclude + } + + [StructLayout(LayoutKind.Sequential)] + private struct IMAGE_NT_HEADERS + { + public uint Signature; + public IMAGE_FILE_HEADER FileHeader; + public IMAGE_OPTIONAL_HEADER OptionalHeader; + } + + private const ushort IMAGE_DOS_SIGNATURE = 0x5A4D; // "MZ" + private const uint IMAGE_NT_SIGNATURE = 0x00004550; // "PE\0\0" + private const int IMAGE_SUBSYSTEM_WINDOWS_GUI = 2; + private const int IMAGE_SUBSYSTEM_WINDOWS_CUI = 3; + internal static bool TryGetType(string filePath, Logger log, out Type type) { type = Type.Unknown; - if (!File.Exists(filePath)) + + if (!File.Exists(filePath)) { + log.SendMessage($"Check binary type - File not found: '{filePath}'.", + TestMessageLevel.Error); return false; + } try { using var stream = new FileStream(filePath, FileMode.Open, FileAccess.Read); - using var peReader = new PEReader(stream); - type = peReader.PEHeaders.PEHeader?.Subsystem switch + using var reader = new BinaryReader(stream); + + if (stream.Length < Marshal.SizeOf<IMAGE_DOS_HEADER>()) { + log.SendMessage("Check binary type - File too small to contain a valid DOS " + + $"header: '{filePath}'.", TestMessageLevel.Error); + return false; + } + + var dosHeader = ReadStruct<IMAGE_DOS_HEADER>(reader); + if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) { + log.SendMessage( + $"Check binary type - Invalid DOS header signature: '{filePath}'." + + $" Expected: {IMAGE_DOS_SIGNATURE}, Actual: {dosHeader.e_magic}", + TestMessageLevel.Error); + return false; + } + + if (dosHeader.e_lfanew < 0 + || dosHeader.e_lfanew > stream.Length - Marshal.SizeOf<IMAGE_NT_HEADERS>()) { + log.SendMessage("Check binary type - Invalid or corrupted NT header offset: " + + $"'{filePath}'. Offset: {dosHeader.e_lfanew}", TestMessageLevel.Error); + return false; + } + + stream.Seek(dosHeader.e_lfanew, SeekOrigin.Begin); + + var ntHeaders = ReadStruct<IMAGE_NT_HEADERS>(reader); + if (ntHeaders.Signature != IMAGE_NT_SIGNATURE) { + log.SendMessage( + $"Check binary type - Invalid NT header signature: '{filePath}'. " + + $"Expected: {IMAGE_NT_SIGNATURE}, Actual: {ntHeaders.Signature}", + TestMessageLevel.Error); + return false; + } + + type = ntHeaders.OptionalHeader.Subsystem switch { - Subsystem.WindowsGui => Type.Gui, - Subsystem.WindowsCui => Type.Console, + IMAGE_SUBSYSTEM_WINDOWS_GUI => Type.Gui, + IMAGE_SUBSYSTEM_WINDOWS_CUI => Type.Console, _ => Type.Unknown }; + } catch (IOException ioException) { + log.SendMessage("IOException was thrown while checking the binary type." + + Environment.NewLine + ioException, TestMessageLevel.Error); + } catch (UnauthorizedAccessException unauthorizedAccessException) { + log.SendMessage( + "UnauthorizedAccessException was thrown while checking the binary type." + + Environment.NewLine + unauthorizedAccessException, TestMessageLevel.Error); } catch (Exception exception) { log.SendMessage("Exception was thrown while checking the binary type." + Environment.NewLine + exception, TestMessageLevel.Error); @@ -41,5 +172,18 @@ namespace QtVsTools.TestAdapter return type != Type.Unknown; } + + private static T ReadStruct<T>(BinaryReader reader) where T : struct + { + var size = Marshal.SizeOf<T>(); + var bytes = reader.ReadBytes(size); + + var handle = GCHandle.Alloc(bytes, GCHandleType.Pinned); + try { + return Marshal.PtrToStructure<T>(handle.AddrOfPinnedObject()); + } finally { + handle.Free(); + } + } } } |
