aboutsummaryrefslogtreecommitdiffstats
path: root/QtVsTools.TestAdapter/Binary.cs
diff options
context:
space:
mode:
authorKarsten Heimrich <karsten.heimrich@qt.io>2024-11-20 18:03:04 +0100
committerKarsten Heimrich <karsten.heimrich@qt.io>2024-11-21 11:46:45 +0000
commit71e14972fc4b2d96cc562afe86dc4feb7dba4cc6 (patch)
treea14828f3ebb10c5a45383794e8482fd248ff6500 /QtVsTools.TestAdapter/Binary.cs
parent2e3bd46aa19a60c863b38bc1a5f8a0c1ceb7fbc9 (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.cs156
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();
+ }
+ }
}
}