]> git.ipfire.org Git - thirdparty/pciutils.git/commitdiff
libpci: win32-kldbg: Implement registration driver from non-native process
authorPali Rohár <pali@kernel.org>
Sat, 18 Jan 2025 18:05:50 +0000 (19:05 +0100)
committerMartin Mares <mj@ucw.cz>
Sun, 8 Jun 2025 15:19:58 +0000 (17:19 +0200)
It is common that 32-bit application is running on 64-bit host system, or
nowadays also that 64-bit AMD64 application is running on ARM64 system.

For all these cases the win32-kldbg.c code horrible fails with just generic
error message when trying to register kldbgdrv.sys driver.

Add code which detects machine type of running process, machine type of
kldbgdrv.sys driver and machine type of the host system. If machine type of
driver and system machines then allow to register driver. Otherwise print
debug verbose message why it is not possible to use kldbgdrv.sys driver
from kd.exe/windbg.exe binary. This could allow to debug issues via command
lspci -G why win32-kldbg refused to load driver from windbg.exe binary.

At the same time relax checks in win32_check_driver() to not depend on the
constants related to process type as process architecture is not relevant
here. Important is always only driver and native system architecture,
process may be running under WoW64 (e.g. i386 process or AMD64 system).

lib/win32-helpers.c
lib/win32-helpers.h
lib/win32-kldbg.c

index 18c9f4a13b7886a4bdf135df1af7d1d5f951d7a7..3b182aa2fef6d7ba927a8d8598f9315fc31f3e3e 100644 (file)
 #define PROCESS_QUERY_LIMITED_INFORMATION 0x1000
 #endif
 
+#ifndef IMAGE_FILE_MACHINE_ARMNT
+#define IMAGE_FILE_MACHINE_ARMNT 0x01c4
+#endif
+#ifndef IMAGE_FILE_MACHINE_IA64
+#define IMAGE_FILE_MACHINE_IA64 0x0200
+#endif
+#ifndef IMAGE_FILE_MACHINE_AMD64
+#define IMAGE_FILE_MACHINE_AMD64 0x8664
+#endif
+#ifndef IMAGE_FILE_MACHINE_ARM64
+#define IMAGE_FILE_MACHINE_ARM64 0xaa64
+#endif
+
+#ifndef PROCESSOR_ARCHITECTURE_INTEL
+#define PROCESSOR_ARCHITECTURE_INTEL 0
+#endif
+#ifndef PROCESSOR_ARCHITECTURE_ARM
+#define PROCESSOR_ARCHITECTURE_ARM 5
+#endif
+#ifndef PROCESSOR_ARCHITECTURE_IA64
+#define PROCESSOR_ARCHITECTURE_IA64 6
+#endif
+#ifndef PROCESSOR_ARCHITECTURE_AMD64
+#define PROCESSOR_ARCHITECTURE_AMD64 9
+#endif
+#ifndef PROCESSOR_ARCHITECTURE_ARM64
+#define PROCESSOR_ARCHITECTURE_ARM64 12
+#endif
+#ifndef PROCESSOR_ARCHITECTURE_UNKNOWN
+#define PROCESSOR_ARCHITECTURE_UNKNOWN 0xffff
+#endif
+
+#if WINVER < 0x0400
+#define wProcessorArchitecture dwOemId
+#endif
+
 /* Unfortunately some toolchains do not provide this constant. */
 #ifndef SE_IMPERSONATE_NAME
 #define SE_IMPERSONATE_NAME TEXT("SeImpersonatePrivilege")
@@ -121,6 +157,23 @@ win32_strerror(DWORD win32_error_id)
   return buffer;
 }
 
+USHORT
+win32_get_process_machine(void)
+{
+  IMAGE_DOS_HEADER *dos_header;
+  IMAGE_NT_HEADERS *nt_header;
+
+  dos_header = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL);
+  if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+    return IMAGE_FILE_MACHINE_UNKNOWN;
+
+  nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
+  if (nt_header->Signature != IMAGE_NT_SIGNATURE)
+    return IMAGE_FILE_MACHINE_UNKNOWN;
+
+  return nt_header->FileHeader.Machine;
+}
+
 BOOL
 win32_is_non_nt_system(void)
 {
@@ -132,12 +185,17 @@ win32_is_non_nt_system(void)
 BOOL
 win32_is_32bit_on_64bit_system(void)
 {
+#ifdef _WIN64
+  return FALSE;
+#else
   BOOL (WINAPI *MyIsWow64Process)(HANDLE, PBOOL);
   HMODULE kernel32;
   BOOL is_wow64;
 
   /*
-   * Check for 64-bit system via IsWow64Process() function exported
+   * 32-bit process running on 64-bit system is called Wow64 process.
+   * So AMD64 process running on ARM64 system is not Wow64 process.
+   * Check for Wow64 process via IsWow64Process() function exported
    * from 32-bit kernel32.dll library available on the 64-bit systems.
    * Resolve pointer to this function at runtime as this code path is
    * primary running on 32-bit systems where are not available 64-bit
@@ -156,6 +214,7 @@ win32_is_32bit_on_64bit_system(void)
     return FALSE;
 
   return is_wow64;
+#endif
 }
 
 BOOL
@@ -178,6 +237,111 @@ win32_is_32bit_on_win8_64bit_system(void)
 #endif
 }
 
+BOOL
+win32_is_not_native_process(USHORT *native_machine_ptr)
+{
+  BOOL (WINAPI *MyIsWow64Process2)(HANDLE, PUSHORT, PUSHORT);
+  void (WINAPI *MyGetNativeSystemInfo)(LPSYSTEM_INFO);
+  SYSTEM_INFO system_info;
+  USHORT process_machine;
+  USHORT native_machine;
+  HMODULE kernel32;
+
+  /*
+   * Process is not native if the process architecture does not match the
+   * native machine architecture. Every Wow64 process is not native (which
+   * means 32-bit process on 64-bit system) but there are also non-Wow64
+   * processes which are not native (e.g. AMD64 process on ARM64 system).
+   */
+
+  kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+  if (!kernel32)
+    return FALSE;
+
+  /*
+   * First try to use IsWow64Process2() function to determinate if the process
+   * is native or not. This function is available since Windows 10.
+   */
+  MyIsWow64Process2 = (void *)GetProcAddress(kernel32, "IsWow64Process2");
+  if (MyIsWow64Process2 && MyIsWow64Process2(GetCurrentProcess(), &process_machine, &native_machine))
+    {
+      /*
+       * Return value from IsWow64Process2() does not indicate if the process
+       * is Wow64, but rather it indicates if the function succeed or not and
+       * filled process_machine and native_machine values.
+       * Process is Wow64 if process_machine is not IMAGE_FILE_MACHINE_UNKNOWN.
+       * For non-Wow64 processes this function does not provide information
+       * about process architecture, so it cannot be used for detecting if the
+       * non-Wow64 process is native or not.
+       */
+      if (process_machine != IMAGE_FILE_MACHINE_UNKNOWN)
+        {
+          if (native_machine_ptr)
+            *native_machine_ptr = native_machine;
+          return TRUE;
+        }
+
+      /*
+       * For non-Wow64 processes retrieve process architecture and compare it
+       * with native machine architecture. This will distinguish between native
+       * and non-native non-Wow64 processes.
+       */
+      process_machine = win32_get_process_machine();
+      if (process_machine != native_machine)
+        {
+          if (native_machine_ptr)
+            *native_machine_ptr = native_machine;
+          return TRUE;
+        }
+      return FALSE;
+    }
+
+  /*
+   * If function IsWow64Process2() is not available or failed then fallback to
+   * IsWow64Process() via win32_is_32bit_on_64bit_system() wrapper. For Wow64
+   * processes is function GetNativeSystemInfo() returning the correct native
+   * machine architecture. For non-Wow64 it is same as GetSystemInfo() and
+   * therefore does NOT return native system information, despite the name
+   * (this happens for example for AMD64 process on ARM64 system).
+   */
+  if (win32_is_32bit_on_64bit_system())
+    {
+      system_info.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_UNKNOWN;
+      MyGetNativeSystemInfo = (void *)GetProcAddress(kernel32, "GetNativeSystemInfo");
+      if (MyGetNativeSystemInfo)
+        MyGetNativeSystemInfo(&system_info);
+      switch (system_info.wProcessorArchitecture)
+        {
+        case PROCESSOR_ARCHITECTURE_INTEL:
+          *native_machine_ptr = IMAGE_FILE_MACHINE_I386;
+          break;
+        case PROCESSOR_ARCHITECTURE_IA64:
+          *native_machine_ptr = IMAGE_FILE_MACHINE_IA64;
+          break;
+        case PROCESSOR_ARCHITECTURE_ARM:
+          *native_machine_ptr = IMAGE_FILE_MACHINE_ARMNT;
+          break;
+        case PROCESSOR_ARCHITECTURE_AMD64:
+          *native_machine_ptr = IMAGE_FILE_MACHINE_AMD64;
+          break;
+        case PROCESSOR_ARCHITECTURE_ARM64:
+          *native_machine_ptr = IMAGE_FILE_MACHINE_ARM64;
+          break;
+        default:
+          *native_machine_ptr = IMAGE_FILE_MACHINE_UNKNOWN;
+        }
+      return TRUE;
+    }
+
+  /*
+   * It looks like that IsWow64Process2() is currently the only function which
+   * can determinate if the non-Wow64 process is native or not. So if the
+   * IsWow64Process2() function is not available (or failed) and process in not
+   * Wow64 then expects that it is native process.
+   */
+  return FALSE;
+}
+
 /*
  * Change error mode of the current thread. If it is not possible then change
  * error mode of the whole process. Always returns previous error mode.
index c415439711b05ba6d4505f29dba607ef014c0cdd..93f2a4b4ec9ec9443655446e0db7d80fabeaede8 100644 (file)
@@ -1,7 +1,9 @@
 const char *win32_strerror(DWORD win32_error_id);
+USHORT win32_get_process_machine(void);
 BOOL win32_is_non_nt_system(void);
 BOOL win32_is_32bit_on_64bit_system(void);
 BOOL win32_is_32bit_on_win8_64bit_system(void);
+BOOL win32_is_not_native_process(USHORT *native_machine);
 UINT win32_change_error_mode(UINT new_mode);
 BOOL win32_have_privilege(LUID luid_privilege);
 BOOL win32_enable_privilege(LUID luid_privilege, HANDLE *revert_token, BOOL *revert_only_privilege);
index ed5b5993d63ada153cc0385282c2bb105f6bfa96..089fbe0977a537451cc226bdc43f1d6fe7961c26 100644 (file)
@@ -125,32 +125,13 @@ static HANDLE kldbg_dev = INVALID_HANDLE_VALUE;
 static BOOL
 win32_kldbg_pci_bus_data(BOOL WriteBusData, USHORT SegmentNumber, BYTE BusNumber, BYTE DeviceNumber, BYTE FunctionNumber, USHORT Address, PVOID Buffer, ULONG BufferSize, LPDWORD Length);
 
-static WORD
-win32_get_current_process_machine(void)
-{
-  IMAGE_DOS_HEADER *dos_header;
-  IMAGE_NT_HEADERS *nt_header;
-
-  dos_header = (IMAGE_DOS_HEADER *)GetModuleHandle(NULL);
-  if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
-    return IMAGE_FILE_MACHINE_UNKNOWN;
-
-  nt_header = (IMAGE_NT_HEADERS *)((BYTE *)dos_header + dos_header->e_lfanew);
-  if (nt_header->Signature != IMAGE_NT_SIGNATURE)
-    return IMAGE_FILE_MACHINE_UNKNOWN;
-
-  return nt_header->FileHeader.Machine;
-}
-
 static BOOL
-win32_check_driver(BYTE *driver_data)
+win32_check_driver(BYTE *driver_data, USHORT native_machine, USHORT *driver_machine_ptr)
 {
   IMAGE_DOS_HEADER *dos_header;
   IMAGE_NT_HEADERS *nt_headers;
-  WORD current_machine;
 
-  current_machine = win32_get_current_process_machine();
-  if (current_machine == IMAGE_FILE_MACHINE_UNKNOWN)
+  if (native_machine == IMAGE_FILE_MACHINE_UNKNOWN)
     return FALSE;
 
   dos_header = (IMAGE_DOS_HEADER *)driver_data;
@@ -161,33 +142,21 @@ win32_check_driver(BYTE *driver_data)
   if (nt_headers->Signature != IMAGE_NT_SIGNATURE)
     return FALSE;
 
-  if (nt_headers->FileHeader.Machine != current_machine)
-    return FALSE;
-
   if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
     return FALSE;
 
-#ifndef _WIN64
-  if (!(nt_headers->FileHeader.Characteristics & IMAGE_FILE_32BIT_MACHINE))
-    return FALSE;
-#endif
-
-  /* IMAGE_OPTIONAL_HEADER is alias for the structure used on the target compiler architecture. */
-  if (nt_headers->FileHeader.SizeOfOptionalHeader < offsetof(IMAGE_OPTIONAL_HEADER, DataDirectory))
-    return FALSE;
-
-  /* IMAGE_NT_OPTIONAL_HDR_MAGIC is alias for the header magic used on the target compiler architecture. */
-  if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)
+  if (nt_headers->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE)
     return FALSE;
 
-  if (nt_headers->OptionalHeader.Subsystem != IMAGE_SUBSYSTEM_NATIVE)
+  *driver_machine_ptr = nt_headers->FileHeader.Machine;
+  if (*driver_machine_ptr != native_machine)
     return FALSE;
 
   return TRUE;
 }
 
-static int
-win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path)
+static BOOL
+win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path, USHORT native_machine)
 {
   BOOL use_kd_exe = FALSE;
   HMODULE exe_with_driver = NULL;
@@ -196,9 +165,10 @@ win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path)
   BYTE *driver_data = NULL;
   DWORD driver_size = 0;
   HANDLE driver_handle = INVALID_HANDLE_VALUE;
+  USHORT driver_machine = IMAGE_FILE_MACHINE_UNKNOWN;
   DWORD written = 0;
   DWORD error = 0;
-  int ret = 0;
+  BOOL ret = FALSE;
 
   /* Try to find and open windbg.exe or kd.exe file in PATH. */
   exe_with_driver = LoadLibraryEx(TEXT("windbg.exe"), NULL, LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
@@ -212,7 +182,7 @@ win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path)
       error = GetLastError();
       if (error == ERROR_FILE_NOT_FOUND ||
           error == ERROR_MOD_NOT_FOUND)
-        a->debug("Cannot find windbg.exe or kd.exe file in PATH");
+        a->debug("Cannot find windbg.exe or kd.exe file in PATH.");
       else
         a->debug("Cannot load %s file: %s.", use_kd_exe ? "kd.exe" : "windbg.exe", win32_strerror(error));
       goto out;
@@ -247,9 +217,14 @@ win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path)
       goto out;
     }
 
-  if (!win32_check_driver(driver_data))
+  if (!win32_check_driver(driver_data, native_machine, &driver_machine))
     {
-      a->debug("Cannot use kldbgdrv.sys driver from %s file: Driver is from different architecture.", use_kd_exe ? "kd.exe" : "windbg.exe");
+      if (native_machine == IMAGE_FILE_MACHINE_UNKNOWN)
+        a->debug("Cannot use kldbgdrv.sys driver from %s file: Cannot determinate native machine architecture.", use_kd_exe ? "kd.exe" : "windbg.exe");
+      else if (driver_machine == IMAGE_FILE_MACHINE_UNKNOWN)
+        a->debug("Cannot use kldbgdrv.sys driver from %s file: Attached resource is not valid kernel driver.", use_kd_exe ? "kd.exe" : "windbg.exe");
+      else
+        a->debug("Cannot use kldbgdrv.sys driver from %s file: Driver machine architecture 0x%04x differs from native machine architecture 0x%04x.", use_kd_exe ? "kd.exe" : "windbg.exe", (unsigned)driver_machine, (unsigned)native_machine);
       goto out;
     }
 
@@ -263,7 +238,7 @@ win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path)
           goto out;
         }
       /* If driver file in system32 directory already exists then treat it as successfull unpack. */
-      ret = 1;
+      ret = TRUE;
       goto out;
     }
 
@@ -279,7 +254,7 @@ win32_kldbg_unpack_driver(struct pci_access *a, LPTSTR driver_path)
     }
 
   a->debug("Driver kldbgdrv.sys was successfully unpacked from %s and stored in system32 directory...", use_kd_exe ? "kd.exe" : "windbg.exe");
-  ret = 1;
+  ret = TRUE;
 
 out:
   if (driver_handle != INVALID_HANDLE_VALUE)
@@ -300,6 +275,13 @@ win32_kldbg_register_driver(struct pci_access *a, SC_HANDLE manager, SC_HANDLE *
   UINT system32_len;
   LPTSTR driver_path;
   HANDLE driver_handle;
+  BOOL has_driver;
+  HMODULE kernel32;
+  USHORT native_machine = IMAGE_FILE_MACHINE_UNKNOWN;
+  BOOL fs_revert_needed = FALSE;
+  PVOID fs_revert_value = NULL;
+  BOOL (WINAPI *MyWow64DisableWow64FsRedirection)(PVOID *) = NULL;
+  BOOL (WINAPI *MyWow64RevertWow64FsRedirection)(PVOID) = NULL;
 
   /*
    * COM library dbgeng.dll unpacks kldbg driver to file "\\system32\\kldbgdrv.sys"
@@ -326,17 +308,80 @@ win32_kldbg_register_driver(struct pci_access *a, SC_HANDLE manager, SC_HANDLE *
 
   memcpy(driver_path + system32_len, TEXT("kldbgdrv.sys"), sizeof(TEXT("kldbgdrv.sys")));
 
-  driver_handle = CreateFile(driver_path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-  if (driver_handle != INVALID_HANDLE_VALUE)
-    CloseHandle(driver_handle);
-  else if (GetLastError() == ERROR_FILE_NOT_FOUND)
+  /*
+   * For non-native processes to access System32 directory, it is required to
+   * disable FsRedirection. FsRedirection is by default enabled and automatically
+   * redirects all access to System32 directory from non-native processes to
+   * different directory (e.g. to SysWOW64 directory).
+   *
+   * FsRedirection can be temporary disabled for he current thread by
+   * Wow64DisableWow64FsRedirection() function and then reverted to the
+   * previous state by Wow64RevertWow64FsRedirection() function. These
+   * functions properly handle repeated calls.
+   */
+
+  if (win32_is_not_native_process(&native_machine))
     {
-      a->debug("Driver kldbgdrv.sys is missing, trying to unpack it from windbg.exe or kd.exe...");
-      if (!win32_kldbg_unpack_driver(a, driver_path))
+      if (native_machine == IMAGE_FILE_MACHINE_UNKNOWN)
         {
+          a->debug("Cannot register new driver: Unable to detect native machine architecture from non-native process.");
           pci_mfree(driver_path);
           return 0;
         }
+      kernel32 = GetModuleHandle(TEXT("kernel32.dll"));
+      if (kernel32)
+        {
+          MyWow64DisableWow64FsRedirection = (void *)GetProcAddress(kernel32, "Wow64DisableWow64FsRedirection");
+          MyWow64RevertWow64FsRedirection = (void *)GetProcAddress(kernel32, "Wow64RevertWow64FsRedirection");
+        }
+      if (!MyWow64DisableWow64FsRedirection || !MyWow64RevertWow64FsRedirection)
+        {
+          a->debug("Cannot register new driver: Unable to locate FsRedirection functions in kernel32.dll library.");
+          pci_mfree(driver_path);
+          return 0;
+        }
+      if (!MyWow64DisableWow64FsRedirection(&fs_revert_value))
+        {
+          a->debug("Cannot register new driver: Disabling FsRedirection for the current thread failed: %s.", win32_strerror(GetLastError()));
+          pci_mfree(driver_path);
+          return 0;
+        }
+      fs_revert_needed = TRUE;
+    }
+  else
+    {
+      native_machine = win32_get_process_machine();
+    }
+
+  has_driver = FALSE;
+  driver_handle = CreateFile(driver_path, 0, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+  if (driver_handle != INVALID_HANDLE_VALUE)
+    CloseHandle(driver_handle);
+
+  if (driver_handle == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND)
+    {
+      a->debug("Driver kldbgdrv.sys is missing, trying to unpack it from windbg.exe or kd.exe...");
+      has_driver = win32_kldbg_unpack_driver(a, driver_path, native_machine);
+    }
+  else
+    {
+      /*
+       * driver_path was either successfully opened or it cannot be opened for
+       * any other reason than ERROR_FILE_NOT_FOUND. So expects that it exists.
+       */
+      has_driver = TRUE;
+    }
+
+  if (fs_revert_needed)
+    {
+      if (!MyWow64RevertWow64FsRedirection(fs_revert_value))
+        a->warning("Reverting of FsRedirection for the current thread failed: %s.", win32_strerror(GetLastError()));
+    }
+
+  if (!has_driver)
+    {
+      pci_mfree(driver_path);
+      return 0;
     }
 
   *service = CreateService(manager, TEXT("kldbgdrv"), TEXT("kldbgdrv"), SERVICE_START, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, driver_path, NULL, NULL, NULL, NULL, NULL);
@@ -392,13 +437,6 @@ win32_kldbg_start_driver(struct pci_access *a)
 
       a->debug("Kernel Local Debugging Driver (kldbgdrv.sys) is not registered, trying to register it...");
 
-      if (win32_is_32bit_on_64bit_system())
-        {
-          /* TODO */
-          a->debug("Registering driver from 32-bit process on 64-bit system is not implemented yet.");
-          goto out;
-        }
-
       if (!win32_kldbg_register_driver(a, manager, &service))
         goto out;
     }