From 76424950e373d3b04ac3dd13019151bfba3e8423 Mon Sep 17 00:00:00 2001 From: sumuel Date: Mon, 17 Aug 2026 20:44:55 +0000 Subject: Add the files --- src/detection/cpuusage/cpuusage_windows.c | 169 ++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 src/detection/cpuusage/cpuusage_windows.c (limited to 'src/detection/cpuusage/cpuusage_windows.c') diff --git a/src/detection/cpuusage/cpuusage_windows.c b/src/detection/cpuusage/cpuusage_windows.c new file mode 100644 index 0000000..c23c28a --- /dev/null +++ b/src/detection/cpuusage/cpuusage_windows.c @@ -0,0 +1,169 @@ +#include "detection/cpuusage/cpuusage.h" +#include "common/mallocHelper.h" +#include "common/debug.h" + +#include +#include +#include +#include "common/windows/perflib_.h" +#include "common/windows/nt.h" + +static const char* getInfoByNqsi(FFlist* cpuTimes) { + ULONG size = 0; + if (NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL, 0, &size) != STATUS_INFO_LENGTH_MISMATCH) { + return "NtQuerySystemInformation(SystemProcessorPerformanceInformation, NULL) failed"; + } + + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* FF_AUTO_FREE pinfo = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*) malloc(size); + if (!NT_SUCCESS(NtQuerySystemInformation(SystemProcessorPerformanceInformation, pinfo, size, &size))) { + return "NtQuerySystemInformation(SystemProcessorPerformanceInformation, size) failed"; + } + + for (uint32_t i = 0; i < size / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); ++i) { + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* coreInfo = pinfo + i; + + // KernelTime includes IdleTime and DpcTime. + coreInfo->KernelTime.QuadPart -= coreInfo->IdleTime.QuadPart; + + uint64_t inUse = (uint64_t) (coreInfo->UserTime.QuadPart + coreInfo->KernelTime.QuadPart); + uint64_t total = inUse + (uint64_t) coreInfo->IdleTime.QuadPart; + + FFCpuUsageInfo* info = FF_LIST_ADD(FFCpuUsageInfo, *cpuTimes); + *info = (FFCpuUsageInfo) { + .inUseAll = inUse, + .totalAll = total, + }; + } + + return NULL; +} + +static const char* getInfoByPerflib(FFlist* cpuTimes) { + static HANDLE hQuery = NULL; + + if (hQuery == NULL) { + struct FFPerfQuerySpec { + PERF_COUNTER_IDENTIFIER Identifier; + WCHAR Name[16]; + } querySpec = { + .Identifier = { + // Processor Information GUID + // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\_V2Providers\{383487a6-3676-4870-a4e7-d45b30c35629}\{b4fc721a-0378-476f-89ba-a5a79f810b36} + .CounterSetGuid = { 0xb4fc721a, 0x0378, 0x476f, { 0x89, 0xba, 0xa5, 0xa7, 0x9f, 0x81, 0x0b, 0x36 } }, + .Size = sizeof(querySpec), + .CounterId = PERF_WILDCARD_COUNTER, // https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data + .InstanceId = PERF_WILDCARD_COUNTER, + }, + .Name = PERF_WILDCARD_INSTANCE, + }; + + if (PerfOpenQueryHandle(NULL, &hQuery) != ERROR_SUCCESS) { + PerfCloseQueryHandle(hQuery); + hQuery = INVALID_HANDLE_VALUE; + return "PerfOpenQueryHandle() failed"; + } + + if (PerfAddCounters(hQuery, &querySpec.Identifier, sizeof(querySpec)) != ERROR_SUCCESS) { + PerfCloseQueryHandle(hQuery); + hQuery = INVALID_HANDLE_VALUE; + return "PerfAddCounters() failed"; + } + + if (querySpec.Identifier.Status != ERROR_SUCCESS) { + PerfCloseQueryHandle(hQuery); + hQuery = INVALID_HANDLE_VALUE; + return "PerfAddCounters() reports invalid identifier"; + } + } + + if (hQuery == INVALID_HANDLE_VALUE) { + return "Init hQuery failed"; + } + + DWORD dataSize = 0; + if (PerfQueryCounterData(hQuery, NULL, 0, &dataSize) != ERROR_NOT_ENOUGH_MEMORY) { + return "PerfQueryCounterData(NULL) failed"; + } + + if (dataSize <= sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER)) { + return "instance doesn't exist"; + } + + FF_AUTO_FREE PERF_DATA_HEADER* const pDataHeader = (PERF_DATA_HEADER*) malloc(dataSize); + if (PerfQueryCounterData(hQuery, pDataHeader, dataSize, &dataSize) != ERROR_SUCCESS) { + return "PerfQueryCounterData(pDataHeader) failed"; + } + + PERF_COUNTER_HEADER* pCounterHeader = (PERF_COUNTER_HEADER*) (pDataHeader + 1); + if (pCounterHeader->dwType != PERF_COUNTERSET) { + return "Invalid counter type"; + } + + PERF_MULTI_COUNTERS* pMultiCounters = (PERF_MULTI_COUNTERS*) (pCounterHeader + 1); + if (pMultiCounters->dwCounters == 0) { + return "No CPU counters found"; + } + + PERF_MULTI_INSTANCES* pMultiInstances = (PERF_MULTI_INSTANCES*) ((BYTE*) pMultiCounters + pMultiCounters->dwSize); + if (pMultiInstances->dwInstances == 0) { + return "No CPU instances found"; + } + + PERF_INSTANCE_HEADER* pInstanceHeader = (PERF_INSTANCE_HEADER*) (pMultiInstances + 1); + for (DWORD iInstance = 0; iInstance < pMultiInstances->dwInstances; ++iInstance) { + const wchar_t* instanceName = (const wchar_t*) ((BYTE*) pInstanceHeader + sizeof(*pInstanceHeader)); + + PERF_COUNTER_DATA* pCounterData = (PERF_COUNTER_DATA*) ((BYTE*) pInstanceHeader + pInstanceHeader->Size); + + uint64_t processorUtility = UINT64_MAX, utilityBase = UINT64_MAX; + for (ULONG iCounter = 0; iCounter != pMultiCounters->dwCounters; iCounter++) { + DWORD* pCounterIds = (DWORD*) (pMultiCounters + 1); + // https://learn.microsoft.com/en-us/windows/win32/perfctrs/using-the-perflib-functions-to-consume-counter-data + switch (pCounterIds[iCounter]) { + case 26: // % Processor Utility (#26, Type=PERF_AVERAGE_BULK) + assert(pCounterData->dwDataSize == sizeof(uint64_t)); + processorUtility = *(uint64_t*) (pCounterData + 1); + break; + case 27: // % Utility Base (#27, Type=PERF_AVERAGE_BASE) + assert(pCounterData->dwDataSize == sizeof(uint32_t)); + utilityBase = *(uint32_t*) (pCounterData + 1) * 100LLU; + break; + } + + pCounterData = (PERF_COUNTER_DATA*) ((BYTE*) pCounterData + pCounterData->dwSize); + } + + if (wcschr(instanceName, L'_') == NULL /* ignore `_Total` */) { + if (processorUtility == UINT64_MAX) { + return "Counter \"% Processor Utility\" are not supported"; + } + + FFCpuUsageInfo* info = FF_LIST_ADD(FFCpuUsageInfo, *cpuTimes); + *info = (FFCpuUsageInfo) { + .inUseAll = processorUtility, + .totalAll = utilityBase, + }; + } + + pInstanceHeader = (PERF_INSTANCE_HEADER*) pCounterData; + } + + return NULL; +} + +const char* ffGetCpuUsageInfo(FFlist* cpuTimes) { + const char* error = NULL; + + if (ffIsWindows10OrGreater()) { + error = getInfoByPerflib(cpuTimes); + FF_DEBUG("Get CPU usage info by Perflib: %s", error ?: "success"); + if (!error) { + return NULL; + } + ffListClear(cpuTimes); + } + + error = getInfoByNqsi(cpuTimes); + FF_DEBUG("Get CPU usage info by NtQuerySystemInformation: %s", error ?: "success"); + return error; +} -- cgit v1.2.3