2026-02-27 15:06:24 +08:00
|
|
|
#include "wm.h"
|
|
|
|
|
#include "common/mallocHelper.h"
|
|
|
|
|
#include "common/io.h"
|
2026-03-03 11:00:05 +08:00
|
|
|
#include "common/library.h"
|
2026-02-28 10:03:27 +08:00
|
|
|
#include "common/processing.h"
|
|
|
|
|
#include "common/windows/nt.h"
|
|
|
|
|
#include "common/windows/unicode.h"
|
|
|
|
|
#include "common/windows/version.h"
|
2026-02-27 15:06:24 +08:00
|
|
|
|
|
|
|
|
#include <stdalign.h>
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#include <ntstatus.h>
|
|
|
|
|
#include <shlobj.h>
|
|
|
|
|
#include <softpub.h>
|
|
|
|
|
|
2026-02-28 10:03:27 +08:00
|
|
|
typedef enum {
|
|
|
|
|
FF_PROCESS_TYPE_NONE,
|
|
|
|
|
FF_PROCESS_TYPE_SIGNED = 1 << 0,
|
|
|
|
|
FF_PROCESS_TYPE_WINDOWS_STORE = 1 << 1,
|
|
|
|
|
FF_PROCESS_TYPE_GUI = 1 << 2,
|
|
|
|
|
FF_PROCESS_TYPE_CUI = 1 << 3,
|
|
|
|
|
} FFProcessType;
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
static bool verifySignature(const wchar_t* filePath) {
|
2026-03-03 11:00:05 +08:00
|
|
|
FF_LIBRARY_LOAD(wintrust, true, "wintrust" FF_LIBRARY_EXTENSION, -1)
|
|
|
|
|
FF_LIBRARY_LOAD_SYMBOL(wintrust, WinVerifyTrustEx, true)
|
|
|
|
|
|
|
|
|
|
WINTRUST_FILE_INFO fileInfo = {
|
|
|
|
|
.cbStruct = sizeof(fileInfo),
|
|
|
|
|
.pcwszFilePath = filePath,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
GUID actionID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
|
|
|
|
|
|
|
|
|
|
WINTRUST_DATA trustData = {
|
|
|
|
|
.cbStruct = sizeof(trustData),
|
|
|
|
|
.dwUIChoice = WTD_UI_NONE,
|
|
|
|
|
.fdwRevocationChecks = WTD_REVOKE_NONE,
|
|
|
|
|
.dwUnionChoice = WTD_CHOICE_FILE,
|
|
|
|
|
.pFile = &fileInfo,
|
|
|
|
|
.dwStateAction = WTD_STATEACTION_VERIFY,
|
|
|
|
|
.dwProvFlags = WTD_SAFER_FLAG,
|
|
|
|
|
};
|
|
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
LONG status = ffWinVerifyTrustEx(nullptr, &actionID, &trustData);
|
2026-03-03 11:00:05 +08:00
|
|
|
trustData.dwStateAction = WTD_STATEACTION_CLOSE;
|
2026-07-10 18:08:04 +08:00
|
|
|
ffWinVerifyTrustEx(nullptr, &actionID, &trustData);
|
2026-03-03 11:00:05 +08:00
|
|
|
|
|
|
|
|
return status == ERROR_SUCCESS;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
static bool isProcessTrusted(DWORD processId, FFProcessType processType, UNICODE_STRING* buffer, size_t bufSize) {
|
2026-07-10 18:08:04 +08:00
|
|
|
FF_AUTO_CLOSE_FD HANDLE hProcess = nullptr;
|
2026-03-16 14:41:52 +08:00
|
|
|
if (!NT_SUCCESS(NtOpenProcess(&hProcess, PROCESS_QUERY_LIMITED_INFORMATION, &(OBJECT_ATTRIBUTES) {
|
2026-03-29 09:28:49 +08:00
|
|
|
.Length = sizeof(OBJECT_ATTRIBUTES),
|
|
|
|
|
},
|
2026-03-30 10:27:23 +08:00
|
|
|
&(CLIENT_ID) { .UniqueProcess = (HANDLE) (uintptr_t) processId }))) {
|
2026-02-27 15:06:24 +08:00
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-02-27 15:06:24 +08:00
|
|
|
|
|
|
|
|
ULONG size;
|
2026-03-29 09:28:49 +08:00
|
|
|
if (!NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageFileNameWin32, buffer, (ULONG) bufSize, &size)) ||
|
|
|
|
|
buffer->Length == 0) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-07-10 18:08:04 +08:00
|
|
|
assert(buffer->MaximumLength >= buffer->Length + 2); // nullptr terminated
|
2026-02-27 15:06:24 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (processType & FF_PROCESS_TYPE_WINDOWS_STORE) {
|
2026-02-28 10:03:27 +08:00
|
|
|
static wchar_t windowsAppsPath[MAX_PATH];
|
|
|
|
|
static uint32_t windowsAppsPathLen;
|
2026-03-29 09:28:49 +08:00
|
|
|
if (windowsAppsPathLen == 0) {
|
2026-07-10 18:08:04 +08:00
|
|
|
PWSTR pPath = nullptr;
|
|
|
|
|
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, nullptr, &pPath))) {
|
2026-02-28 10:03:27 +08:00
|
|
|
windowsAppsPathLen = (uint32_t) wcslen(pPath);
|
|
|
|
|
memcpy(windowsAppsPath, pPath, windowsAppsPathLen * sizeof(wchar_t));
|
|
|
|
|
memcpy(windowsAppsPath + windowsAppsPathLen, L"\\WindowsApps\\", sizeof(L"\\WindowsApps\\"));
|
|
|
|
|
windowsAppsPathLen += strlen("\\WindowsApps\\");
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2026-02-28 10:03:27 +08:00
|
|
|
windowsAppsPathLen = -1u;
|
|
|
|
|
}
|
|
|
|
|
CoTaskMemFree(pPath);
|
2026-02-27 15:06:24 +08:00
|
|
|
}
|
2026-02-28 10:03:27 +08:00
|
|
|
if (windowsAppsPathLen != -1u &&
|
2026-03-29 09:28:49 +08:00
|
|
|
(buffer->Length <= windowsAppsPathLen * sizeof(wchar_t) || // Path is too short to be in WindowsApps
|
|
|
|
|
_wcsnicmp(buffer->Buffer, windowsAppsPath, windowsAppsPathLen) != 0) // Path does not start with WindowsApps
|
|
|
|
|
) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-02-27 15:06:24 +08:00
|
|
|
}
|
2026-02-28 10:03:27 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (processType & FF_PROCESS_TYPE_SIGNED) {
|
|
|
|
|
if (!verifySignature(buffer->Buffer)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-02-28 10:03:27 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (processType & (FF_PROCESS_TYPE_GUI | FF_PROCESS_TYPE_CUI)) {
|
2026-02-28 10:03:27 +08:00
|
|
|
SECTION_IMAGE_INFORMATION info = {};
|
2026-03-29 09:28:49 +08:00
|
|
|
if (!NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageInformation, &info, sizeof(info), &size)) ||
|
|
|
|
|
size != sizeof(info)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2026-02-28 10:03:27 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if ((processType & FF_PROCESS_TYPE_GUI) && info.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) {
|
2026-02-28 10:03:27 +08:00
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
|
|
|
|
if ((processType & FF_PROCESS_TYPE_CUI) && info.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI) {
|
2026-02-28 10:03:27 +08:00
|
|
|
return false;
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-02-28 10:03:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
2026-02-27 15:06:24 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
#define ffStrEqualNWS(str, compareTo) (_wcsnicmp(str, L##compareTo, sizeof(compareTo) - 1) == 0)
|
2026-03-06 14:36:40 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
const char* ffDetectWMPlugin(FFstrbuf* pluginName) {
|
2026-02-27 15:06:24 +08:00
|
|
|
alignas(UNICODE_STRING) uint8_t buffer[4096];
|
|
|
|
|
UNICODE_STRING* filePath = (UNICODE_STRING*) buffer;
|
2026-07-10 18:08:04 +08:00
|
|
|
SYSTEM_PROCESS_INFORMATION* FF_AUTO_FREE pstart = nullptr;
|
2026-02-27 15:06:24 +08:00
|
|
|
|
|
|
|
|
// Multiple attempts in case processes change while
|
|
|
|
|
// we are in the middle of querying them.
|
|
|
|
|
ULONG size = 0;
|
2026-03-29 09:28:49 +08:00
|
|
|
for (int attempts = 0;; ++attempts) {
|
|
|
|
|
if (size) {
|
|
|
|
|
pstart = (SYSTEM_PROCESS_INFORMATION*) realloc(pstart, size);
|
2026-02-27 15:06:24 +08:00
|
|
|
assert(pstart);
|
|
|
|
|
}
|
|
|
|
|
NTSTATUS status = NtQuerySystemInformation(SystemProcessInformation, pstart, size, &size);
|
2026-03-29 09:28:49 +08:00
|
|
|
if (NT_SUCCESS(status)) {
|
2026-02-27 15:06:24 +08:00
|
|
|
break;
|
2026-03-29 09:28:49 +08:00
|
|
|
} else if (status == STATUS_INFO_LENGTH_MISMATCH && attempts < 4) {
|
2026-02-27 15:06:24 +08:00
|
|
|
size += sizeof(SYSTEM_PROCESS_INFORMATION) * 5;
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2026-02-27 15:06:24 +08:00
|
|
|
return "NtQuerySystemInformation(SystemProcessInformation) failed";
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-02-27 15:06:24 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
for (SYSTEM_PROCESS_INFORMATION* ptr = pstart;; ptr = (SYSTEM_PROCESS_INFORMATION*) ((uint8_t*) ptr + ptr->NextEntryOffset)) {
|
2026-07-10 18:08:04 +08:00
|
|
|
assert(ptr->ImageName.Length == 0 || ptr->ImageName.MaximumLength >= ptr->ImageName.Length + 2); // nullptr terminated
|
2026-02-27 15:06:24 +08:00
|
|
|
if (ptr->ImageName.Length == strlen("FancyWM-GUI.exe") * sizeof(wchar_t) &&
|
2026-03-06 14:36:40 +08:00
|
|
|
ffStrEqualNWS(ptr->ImageName.Buffer, "FancyWM-GUI.exe") &&
|
2026-03-29 09:28:49 +08:00
|
|
|
isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_WINDOWS_STORE | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) {
|
2026-07-10 18:08:04 +08:00
|
|
|
if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) {
|
2026-02-27 15:06:24 +08:00
|
|
|
ffStrbufPrependS(pluginName, "FancyWM ");
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2026-02-27 15:06:24 +08:00
|
|
|
ffStrbufSetStatic(pluginName, "FancyWM");
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-02-27 15:06:24 +08:00
|
|
|
break;
|
2026-03-29 09:28:49 +08:00
|
|
|
} else if (ptr->ImageName.Length == strlen("glazewm-watcher.exe") * sizeof(wchar_t) &&
|
2026-03-06 14:36:40 +08:00
|
|
|
ffStrEqualNWS(ptr->ImageName.Buffer, "glazewm-watcher.exe") &&
|
2026-03-29 09:28:49 +08:00
|
|
|
isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_SIGNED | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer))) {
|
2026-07-10 18:08:04 +08:00
|
|
|
if (instance.config.general.detectVersion && ffGetFileVersion(filePath->Buffer, nullptr, pluginName)) {
|
2026-02-27 15:06:24 +08:00
|
|
|
ffStrbufPrependS(pluginName, "GlazeWM ");
|
2026-03-29 09:28:49 +08:00
|
|
|
} else {
|
2026-02-27 15:06:24 +08:00
|
|
|
ffStrbufSetStatic(pluginName, "GlazeWM");
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-02-27 15:06:24 +08:00
|
|
|
break;
|
2026-03-29 09:28:49 +08:00
|
|
|
} else if (ptr->ImageName.Length == strlen("komorebi.exe") * sizeof(wchar_t) &&
|
2026-03-06 14:36:40 +08:00
|
|
|
ffStrEqualNWS(ptr->ImageName.Buffer, "komorebi.exe") &&
|
2026-03-29 09:28:49 +08:00
|
|
|
isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_CUI, filePath, sizeof(buffer))) {
|
|
|
|
|
if (instance.config.general.detectVersion) {
|
2026-03-06 11:10:35 +08:00
|
|
|
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateNWS(filePath->Length / sizeof(wchar_t), filePath->Buffer);
|
2026-03-29 09:28:49 +08:00
|
|
|
if (ffProcessAppendStdOut(pluginName, (char* const[]) {
|
|
|
|
|
path.chars,
|
|
|
|
|
"--version",
|
2026-07-10 18:08:04 +08:00
|
|
|
nullptr,
|
|
|
|
|
}) == nullptr) {
|
2026-03-06 11:10:35 +08:00
|
|
|
ffStrbufSubstrBeforeFirstC(pluginName, '\n');
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-03-06 11:10:35 +08:00
|
|
|
}
|
2026-03-29 09:28:49 +08:00
|
|
|
if (pluginName->length == 0) {
|
2026-02-28 10:03:27 +08:00
|
|
|
ffStrbufSetStatic(pluginName, "Komorebi");
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-02-28 10:03:27 +08:00
|
|
|
break;
|
|
|
|
|
}
|
2026-03-06 14:36:40 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (ptr->NextEntryOffset == 0) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-02-27 15:06:24 +08:00
|
|
|
}
|
|
|
|
|
|
2026-07-10 18:08:04 +08:00
|
|
|
return nullptr;
|
2026-02-27 15:06:24 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-09 16:20:59 +08:00
|
|
|
const char* ffDetectWMVersion(const FFstrbuf* wmName, FFstrbuf* result, FF_A_UNUSED FFWMOptions* options) {
|
2026-03-29 09:28:49 +08:00
|
|
|
if (!wmName) {
|
2026-02-27 15:06:24 +08:00
|
|
|
return "No WM detected";
|
2026-03-29 09:28:49 +08:00
|
|
|
}
|
2026-02-27 15:06:24 +08:00
|
|
|
|
2026-03-29 09:28:49 +08:00
|
|
|
if (ffStrbufEqualS(wmName, "dwm.exe")) {
|
2026-07-10 18:08:04 +08:00
|
|
|
PWSTR pPath = nullptr;
|
|
|
|
|
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_System, KF_FLAG_DEFAULT, nullptr, &pPath))) {
|
2026-02-27 15:06:24 +08:00
|
|
|
wchar_t fullPath[MAX_PATH];
|
|
|
|
|
wcscpy(fullPath, pPath);
|
|
|
|
|
wcscat(fullPath, L"\\dwm.exe");
|
2026-07-10 18:08:04 +08:00
|
|
|
ffGetFileVersion(fullPath, nullptr, result);
|
2026-02-27 15:06:24 +08:00
|
|
|
}
|
|
|
|
|
CoTaskMemFree(pPath);
|
2026-07-10 18:08:04 +08:00
|
|
|
return nullptr;
|
2026-02-27 15:06:24 +08:00
|
|
|
}
|
|
|
|
|
return "Not supported on this platform";
|
|
|
|
|
}
|