From 3e25b213b1019fdb1987f849d833f243dfce4f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=80=9A=E6=B4=B2?= Date: Sat, 28 Feb 2026 10:03:27 +0800 Subject: [PATCH] WM (Windows): supports komorebi detection --- src/common/windows/nt.h | 51 ++++++++++++++ src/detection/wm/wm_windows.c | 127 +++++++++++++++++++++++----------- 2 files changed, 138 insertions(+), 40 deletions(-) diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h index 732106e47..b9e14bcc3 100644 --- a/src/common/windows/nt.h +++ b/src/common/windows/nt.h @@ -278,3 +278,54 @@ typedef struct _CURDIR } CURDIR, *PCURDIR; PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader(IN PVOID BaseOfImage); + +/** + * The SECTION_IMAGE_INFORMATION structure contains detailed information about an image section. + */ +typedef struct _SECTION_IMAGE_INFORMATION +{ + PVOID TransferAddress; // The address of the image entry point function. + ULONG ZeroBits; // The number of high-order address bits that must be zero in the image base address. + SIZE_T MaximumStackSize; // The maximum stack size of threads from the PE file header. + SIZE_T CommittedStackSize; // The initial stack size of threads from the PE file header. + ULONG SubSystemType; // The image subsystem from the PE file header (e.g., Windows GUI, Windows CUI, POSIX). + union + { + struct + { + USHORT SubSystemMinorVersion; + USHORT SubSystemMajorVersion; + }; + ULONG SubSystemVersion; + }; + union + { + struct + { + USHORT MajorOperatingSystemVersion; + USHORT MinorOperatingSystemVersion; + }; + ULONG OperatingSystemVersion; + }; + USHORT ImageCharacteristics; // The image characteristics from the PE file header. + USHORT DllCharacteristics; // The DLL characteristics flags (e.g., ASLR, NX compatibility). + USHORT Machine; // The image architecture (e.g., x86, x64, ARM). + BOOLEAN ImageContainsCode; // The image contains native executable code. + union + { + UCHAR ImageFlags; + struct + { + UCHAR ComPlusNativeReady : 1; // The image contains precompiled .NET assembly generated by NGEN (Native Image Generator). + UCHAR ComPlusILOnly : 1; // the image contains only Microsoft Intermediate Language (IL) assembly. + UCHAR ImageDynamicallyRelocated : 1; // The image was mapped using a random base address rather than the preferred base address. + UCHAR ImageMappedFlat : 1; // The image was mapped using a single contiguous region, rather than separate regions for each section. + UCHAR BaseBelow4gb : 1; // The image was mapped using a base address below the 4 GB boundary. + UCHAR ComPlusPrefer32bit : 1; // The image prefers to run as a 32-bit process, even on a 64-bit system. + UCHAR Reserved : 2; + }; + }; + ULONG LoaderFlags; // Reserved by ntdll.dll for the Windows loader. + ULONG ImageFileSize; // The size of the image, in bytes, including all headers. + ULONG CheckSum; // The image file checksum, from the PE optional header. +} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION; diff --git a/src/detection/wm/wm_windows.c b/src/detection/wm/wm_windows.c index 9b5f850a0..141252bcd 100644 --- a/src/detection/wm/wm_windows.c +++ b/src/detection/wm/wm_windows.c @@ -1,7 +1,10 @@ #include "wm.h" #include "common/mallocHelper.h" -#include "common/windows/version.h" #include "common/io.h" +#include "common/processing.h" +#include "common/windows/nt.h" +#include "common/windows/unicode.h" +#include "common/windows/version.h" #include #include @@ -10,7 +13,15 @@ #include #include -bool isProcessTrusted(DWORD processId, UNICODE_STRING* buffer, size_t bufSize) +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; + +bool isProcessTrusted(DWORD processId, FFProcessType processType, UNICODE_STRING* buffer, size_t bufSize) { FF_AUTO_CLOSE_FD HANDLE hProcess = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, processId); if (!hProcess) @@ -21,51 +32,72 @@ bool isProcessTrusted(DWORD processId, UNICODE_STRING* buffer, size_t bufSize) buffer->Length == 0) return false; assert(buffer->MaximumLength >= buffer->Length + 2); // NULL terminated - static wchar_t windowsAppsPath[MAX_PATH]; - static uint32_t windowsAppsPathLen; - if (windowsAppsPathLen == 0) + if (processType & FF_PROCESS_TYPE_WINDOWS_STORE) { - PWSTR pPath = NULL; - if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &pPath))) + static wchar_t windowsAppsPath[MAX_PATH]; + static uint32_t windowsAppsPathLen; + if (windowsAppsPathLen == 0) { - windowsAppsPathLen = (uint32_t) wcslen(pPath); - memcpy(windowsAppsPath, pPath, windowsAppsPathLen * sizeof(wchar_t)); - memcpy(windowsAppsPath + windowsAppsPathLen, L"\\WindowsApps\\", sizeof(L"\\WindowsApps\\")); - windowsAppsPathLen += strlen("\\WindowsApps\\"); + PWSTR pPath = NULL; + if(SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_ProgramFiles, KF_FLAG_DEFAULT, NULL, &pPath))) + { + windowsAppsPathLen = (uint32_t) wcslen(pPath); + memcpy(windowsAppsPath, pPath, windowsAppsPathLen * sizeof(wchar_t)); + memcpy(windowsAppsPath + windowsAppsPathLen, L"\\WindowsApps\\", sizeof(L"\\WindowsApps\\")); + windowsAppsPathLen += strlen("\\WindowsApps\\"); + } + else + { + windowsAppsPathLen = -1u; + } + CoTaskMemFree(pPath); } - else - { - windowsAppsPathLen = -1u; - } - CoTaskMemFree(pPath); + if (windowsAppsPathLen != -1u && + buffer->Length > windowsAppsPathLen * sizeof(wchar_t) && + _wcsnicmp(buffer->Buffer, windowsAppsPath, windowsAppsPathLen) != 0 + ) return false; } - if (windowsAppsPathLen != -1u && - buffer->Length > windowsAppsPathLen * sizeof(wchar_t) && - _wcsnicmp(buffer->Buffer, windowsAppsPath, windowsAppsPathLen) == 0 - ) return true; // Always trust Windows Store apps, in case the exe is not signed (FancyWM-GUI) - WINTRUST_FILE_INFO fileInfo = { - .cbStruct = sizeof(fileInfo), - .pcwszFilePath = buffer->Buffer, - }; + if (processType & FF_PROCESS_TYPE_SIGNED) + { + WINTRUST_FILE_INFO fileInfo = { + .cbStruct = sizeof(fileInfo), + .pcwszFilePath = buffer->Buffer, + }; - GUID actionID = WINTRUST_ACTION_GENERIC_VERIFY_V2; + 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, - }; + 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, + }; - LONG status = WinVerifyTrustEx(NULL, &actionID, &trustData); - trustData.dwStateAction = WTD_STATEACTION_CLOSE; - WinVerifyTrust(NULL, &actionID, &trustData); + LONG status = WinVerifyTrustEx(NULL, &actionID, &trustData); + trustData.dwStateAction = WTD_STATEACTION_CLOSE; + WinVerifyTrust(NULL, &actionID, &trustData); - return status == ERROR_SUCCESS; + if (status != ERROR_SUCCESS) + return false; + } + + if (processType & (FF_PROCESS_TYPE_GUI | FF_PROCESS_TYPE_CUI)) + { + SECTION_IMAGE_INFORMATION info = {}; + if(!NT_SUCCESS(NtQueryInformationProcess(hProcess, ProcessImageInformation, &info, sizeof(info), &size)) || + size != sizeof(info)) return false; + + if ((processType & FF_PROCESS_TYPE_GUI) && info.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_GUI) + return false; + if ((processType & FF_PROCESS_TYPE_CUI) && info.SubSystemType != IMAGE_SUBSYSTEM_WINDOWS_CUI) + return false; + } + + return true; } const char* ffDetectWMPlugin(FFstrbuf* pluginName) @@ -98,7 +130,7 @@ const char* ffDetectWMPlugin(FFstrbuf* pluginName) assert(ptr->ImageName.Length == 0 || ptr->ImageName.MaximumLength >= ptr->ImageName.Length + 2); // NULL terminated if (ptr->ImageName.Length == strlen("FancyWM-GUI.exe") * sizeof(wchar_t) && memcmp(ptr->ImageName.Buffer, L"FancyWM-GUI.exe", ptr->ImageName.Length) == 0 && - isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, filePath, sizeof(buffer)) + isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_WINDOWS_STORE | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer)) ) { if (ffGetFileVersion(filePath->Buffer, NULL, pluginName)) ffStrbufPrependS(pluginName, "FancyWM "); @@ -108,7 +140,7 @@ const char* ffDetectWMPlugin(FFstrbuf* pluginName) } else if (ptr->ImageName.Length == strlen("glazewm-watcher.exe") * sizeof(wchar_t) && memcmp(ptr->ImageName.Buffer, L"glazewm-watcher.exe", ptr->ImageName.Length) == 0 && - isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, filePath, sizeof(buffer)) + isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_SIGNED | FF_PROCESS_TYPE_GUI, filePath, sizeof(buffer)) ) { if (ffGetFileVersion(filePath->Buffer, NULL, pluginName)) ffStrbufPrependS(pluginName, "GlazeWM "); @@ -116,6 +148,21 @@ const char* ffDetectWMPlugin(FFstrbuf* pluginName) ffStrbufSetStatic(pluginName, "GlazeWM"); break; } + else if (ptr->ImageName.Length == strlen("komorebi.exe") * sizeof(wchar_t) && + memcmp(ptr->ImageName.Buffer, L"komorebi.exe", ptr->ImageName.Length) == 0 && + isProcessTrusted((DWORD) (uintptr_t) ptr->UniqueProcessId, FF_PROCESS_TYPE_CUI, filePath, sizeof(buffer)) + ) { + FF_STRBUF_AUTO_DESTROY path = ffStrbufCreateNWS(filePath->Length / sizeof(wchar_t), filePath->Buffer); + if (ffProcessAppendStdOut(pluginName, (char *const[]) { + path.chars, + "--version", + NULL, + }) == NULL) + ffStrbufSubstrBeforeFirstC(pluginName, '\n'); + else + ffStrbufSetStatic(pluginName, "Komorebi"); + break; + } } return NULL;