From 00af2d1df259a5d19e2e381c3d5c9fea47f85acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Wed, 2 Sep 2026 16:12:10 +0800 Subject: [PATCH] WM: uses SystemBasicProcessInformation if available --- src/detection/wm/wm_windows.c | 150 +++++++++++++++++++++------------- 1 file changed, 95 insertions(+), 55 deletions(-) diff --git a/src/detection/wm/wm_windows.c b/src/detection/wm/wm_windows.c index 01a336b28..21e073dd2 100644 --- a/src/detection/wm/wm_windows.c +++ b/src/detection/wm/wm_windows.c @@ -114,70 +114,110 @@ static bool isProcessTrusted(DWORD processId, FFProcessType processType, UNICODE #define ffStrEqualNWS(str, compareTo) (_wcsnicmp(str, L##compareTo, sizeof(compareTo) - 1) == 0) -const char* ffDetectWMPlugin(FFstrbuf* pluginName) { +// Returns true if the process was recognized as a WM plugin and pluginName was set. +static bool handleProcess(FFstrbuf* pluginName, uint32_t pid, const UNICODE_STRING* imageName) { alignas(UNICODE_STRING) uint8_t buffer[4096]; UNICODE_STRING* filePath = (UNICODE_STRING*) buffer; - FF_AUTO_FREE SYSTEM_PROCESS_INFORMATION* pstart = nullptr; - // Multiple attempts in case processes change while - // we are in the middle of querying them. - ULONG size = 0; - for (int attempts = 0;; ++attempts) { - if (size) { - pstart = (SYSTEM_PROCESS_INFORMATION*) realloc(pstart, size); - assert(pstart); - } - NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, pstart, size, &size); - if (NT_SUCCESS(status)) { - break; - } else if (status == STATUS_INFO_LENGTH_MISMATCH && attempts < 4) { - size += sizeof(SYSTEM_PROCESS_INFORMATION) * 5; + if (imageName->Length == strlen("FancyWM-GUI.exe") * sizeof(wchar_t) && + ffStrEqualNWS(imageName->Buffer, "FancyWM-GUI.exe") && + isProcessTrusted(pid, FF_PROCESS_TYPE_WINDOWS_STORE | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) { + if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) { + ffStrbufPrependS(pluginName, "FancyWM "); } else { - return "NtQuerySystemInformation(SystemProcessInformation) failed"; + ffStrbufSetStatic(pluginName, "FancyWM"); } + return true; + } else if (imageName->Length == strlen("glazewm-watcher.exe") * sizeof(wchar_t) && + ffStrEqualNWS(imageName->Buffer, "glazewm-watcher.exe") && + isProcessTrusted(pid, FF_PROCESS_TYPE_SIGNED | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) { + if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) { + ffStrbufPrependS(pluginName, "GlazeWM "); + } else { + ffStrbufSetStatic(pluginName, "GlazeWM"); + } + return true; + } else if (imageName->Length == strlen("komorebi.exe") * sizeof(wchar_t) && + ffStrEqualNWS(imageName->Buffer, "komorebi.exe") && + isProcessTrusted(pid, FF_PROCESS_TYPE_CUI, filePath, sizeof(buffer))) { + if (instance.config.general.detectVersion) { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateNWS(filePath->Length / sizeof(wchar_t), filePath->Buffer); + if (ffProcessAppendStdOut(pluginName, (char* const[]) { + path.chars, + "--version", + nullptr, + }) == nullptr) { + ffStrbufSubstrBeforeFirstC(pluginName, '\n'); + } + } + if (pluginName->length == 0) { + ffStrbufSetStatic(pluginName, "Komorebi"); + } + return true; } - for (SYSTEM_PROCESS_INFORMATION* ptr = pstart;; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) { - assert(ptr->ImageName.Length == 0 || ptr->ImageName.MaximumLength >= ptr->ImageName.Length + 2); // nullptr terminated - if (ptr->ImageName.Length == strlen("FancyWM-GUI.exe") * sizeof(wchar_t) && - ffStrEqualNWS(ptr->ImageName.Buffer, "FancyWM-GUI.exe") && - isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_WINDOWS_STORE | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) { - if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) { - ffStrbufPrependS(pluginName, "FancyWM "); - } else { - ffStrbufSetStatic(pluginName, "FancyWM"); - } - break; - } else if (ptr->ImageName.Length == strlen("glazewm-watcher.exe") * sizeof(wchar_t) && - ffStrEqualNWS(ptr->ImageName.Buffer, "glazewm-watcher.exe") && - isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_SIGNED | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) { - if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) { - ffStrbufPrependS(pluginName, "GlazeWM "); - } else { - ffStrbufSetStatic(pluginName, "GlazeWM"); - } - break; - } else if (ptr->ImageName.Length == strlen("komorebi.exe") * sizeof(wchar_t) && - ffStrEqualNWS(ptr->ImageName.Buffer, "komorebi.exe") && - isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_CUI, filePath, sizeof(buffer))) { - if (instance.config.general.detectVersion) { - FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateNWS(filePath->Length / sizeof(wchar_t), filePath->Buffer); - if (ffProcessAppendStdOut(pluginName, (char* const[]) { - path.chars, - "--version", - nullptr, - }) == nullptr) { - ffStrbufSubstrBeforeFirstC(pluginName, '\n'); - } - } - if (pluginName->length == 0) { - ffStrbufSetStatic(pluginName, "Komorebi"); - } - break; + return false; +} + +const char* ffDetectWMPlugin(FFstrbuf* pluginName) { + if (ffIsSystemBasicProcessInfoAvailable()) { + // Unlike SystemProcessInformation, SystemBasicProcessInformation only returns processes + // (no threads), so a single query with a modest buffer is enough. + ULONG size = 0; + if (NtQuerySystemInformation(SystemBasicProcessInformation, nullptr, 0, &size) != STATUS_INFO_LENGTH_MISMATCH) { + goto fallback; + } + // The process table may change between the two calls; retry with a larger buffer. + size += size / 8 + sizeof(SYSTEM_BASICPROCESS_INFORMATION); + FF_AUTO_FREE SYSTEM_BASICPROCESS_INFORMATION* pstart = malloc(size); + + if (!NT_SUCCESS(NtQuerySystemInformation(SystemBasicProcessInformation, pstart, size, &size))) { + goto fallback; } - if (ptr->NextEntryOffset == 0) { - break; + for (auto ptr = pstart;; ptr = (SYSTEM_BASICPROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) { + assert(ptr >= pstart && (uint8_t*) ptr < (uint8_t*) pstart + size); + if (handleProcess(pluginName, (uint32_t)(uintptr_t) ptr->UniqueProcessId, &ptr->ImageName)) { + return nullptr; + } + // The last process in the list always has a NextEntryOffset of 0, even if the buffer was truncated. + if (!ptr->NextEntryOffset) { + break; + } + } + return nullptr; + } + +fallback: + { + FF_AUTO_FREE SYSTEM_PROCESS_INFORMATION* pstart = nullptr; + + // Multiple attempts in case processes change while + // we are in the middle of querying them. + ULONG size = 0; + for (int attempts = 0;; ++attempts) { + if (size) { + pstart = (SYSTEM_PROCESS_INFORMATION*) realloc(pstart, size); + assert(pstart); + } + NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, pstart, size, &size); + if (NT_SUCCESS(status)) { + break; + } else if (status == STATUS_INFO_LENGTH_MISMATCH && attempts < 4) { + size += sizeof(SYSTEM_PROCESS_INFORMATION) * 5; + } else { + return "NtQuerySystemInformation(SystemProcessInformation) failed"; + } + } + + for (auto ptr = pstart;; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) { + assert(ptr->ImageName.Length == 0 || ptr->ImageName.MaximumLength >= ptr->ImageName.Length + 2); // nullptr terminated + if (handleProcess(pluginName, (uint32_t)(uintptr_t) ptr->UniqueProcessId, &ptr->ImageName)) { + return nullptr; + } + if (ptr->NextEntryOffset == 0) { + break; + } } }