From 8400e15b80c0e3d4b32acd32bd3c4be5c52c15c0 Mon Sep 17 00:00:00 2001 From: Brennan Date: Mon, 19 May 2025 20:40:55 -0700 Subject: [PATCH] [IIS] Manually parse exe bitness (#61894) --- .../CommonLib/HostFxrResolver.cpp | 72 +++++++++++++++---- .../CommonLib/HostFxrResolver.h | 2 + src/Servers/IIS/build/Build.Lib.Settings | 2 +- 3 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp index 9b12cd0132b4..8fb960261590 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.cpp @@ -197,7 +197,7 @@ HostFxrResolver::TryGetHostFxrPath( size_t size = MAX_PATH * 2; hostfxrPath.resize(size); - get_hostfxr_parameters params; + get_hostfxr_parameters params{}; params.size = sizeof(get_hostfxr_parameters); params.assembly_path = applicationPath.c_str(); params.dotnet_root = dotnetRoot.c_str(); @@ -393,7 +393,7 @@ HostFxrResolver::GetAbsolutePathToDotnetFromHostfxr(const fs::path& hostfxrPath) // Tries to call where.exe to find the location of dotnet.exe. // Will check that the bitness of dotnet matches the current // worker process bitness. -// Returns true if a valid dotnet was found, else false.R +// Returns true if a valid dotnet was found, else false. // std::optional HostFxrResolver::InvokeWhereToFindDotnet() @@ -415,8 +415,7 @@ HostFxrResolver::InvokeWhereToFindDotnet() DWORD dwExitCode; STRU struDotnetSubstring; STRU struDotnetLocationsString; - DWORD dwNumBytesRead; - DWORD dwBinaryType; + DWORD dwNumBytesRead = 0; INT index = 0; INT prevIndex = 0; std::optional result; @@ -521,14 +520,7 @@ HostFxrResolver::InvokeWhereToFindDotnet() LOG_INFOF(L"Processing entry '%ls'", struDotnetSubstring.QueryStr()); - if (LOG_LAST_ERROR_IF(!GetBinaryTypeW(struDotnetSubstring.QueryStr(), &dwBinaryType))) - { - continue; - } - - LOG_INFOF(L"Binary type %d", dwBinaryType); - - if (fIsCurrentProcess64Bit == (dwBinaryType == SCS_64BIT_BINARY)) + if (fIsCurrentProcess64Bit == IsX64(struDotnetSubstring.QueryStr())) { // The bitness of dotnet matched with the current worker process bitness. return std::make_optional(struDotnetSubstring.QueryStr()); @@ -539,6 +531,62 @@ HostFxrResolver::InvokeWhereToFindDotnet() return result; } +BOOL HostFxrResolver::IsX64(const WCHAR* dotnetPath) +{ + // Errors while reading from the file shouldn't throw unless + // file.exception(bits) is set + std::ifstream file(dotnetPath, std::ios::binary); + if (!file.is_open()) + { + LOG_TRACEF(L"Failed to open file %ls", dotnetPath); + return false; + } + + // Read the DOS header + IMAGE_DOS_HEADER dosHeader{}; + file.read(reinterpret_cast(&dosHeader), sizeof(dosHeader)); + if (dosHeader.e_magic != IMAGE_DOS_SIGNATURE) // 'MZ' + { + LOG_TRACEF(L"%ls is not a valid executable file (missing MZ header).", dotnetPath); + return false; + } + + // Seek to the PE header + file.seekg(dosHeader.e_lfanew, std::ios::beg); + + // Read the PE header + DWORD peSignature{}; + file.read(reinterpret_cast(&peSignature), sizeof(peSignature)); + if (peSignature != IMAGE_NT_SIGNATURE) // 'PE\0\0' + { + LOG_TRACEF(L"%ls is not a valid PE file (missing PE header).", dotnetPath); + return false; + } + + // Read the file header + IMAGE_FILE_HEADER fileHeader{}; + file.read(reinterpret_cast(&fileHeader), sizeof(fileHeader)); + + // Read the optional header magic field + WORD magic{}; + file.read(reinterpret_cast(&magic), sizeof(magic)); + + // Determine the architecture based on the magic value + if (magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC) + { + LOG_INFOF(L"%ls is 32-bit", dotnetPath); + return false; + } + else if (magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) + { + LOG_INFOF(L"%ls is 64-bit", dotnetPath); + return true; + } + + LOG_INFOF(L"%ls is unknown architecture %i", dotnetPath, fileHeader.Machine); + return false; +} + std::optional HostFxrResolver::GetAbsolutePathToDotnetFromProgramFiles() { diff --git a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h index 519f6df52c97..08ec650aec54 100644 --- a/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h +++ b/src/Servers/IIS/AspNetCoreModuleV2/CommonLib/HostFxrResolver.h @@ -74,6 +74,8 @@ class HostFxrResolver const std::filesystem::path & requestedPath ); + static BOOL IsX64(const WCHAR* dotnetPath); + struct LocalFreeDeleter { void operator ()(_In_ LPWSTR* ptr) const diff --git a/src/Servers/IIS/build/Build.Lib.Settings b/src/Servers/IIS/build/Build.Lib.Settings index 0dcba8c2011a..9327eb363771 100644 --- a/src/Servers/IIS/build/Build.Lib.Settings +++ b/src/Servers/IIS/build/Build.Lib.Settings @@ -9,7 +9,7 @@ - false + true _LIB;%(PreprocessorDefinitions) true