From 2dbbf88540ecbf72d649e732f614095c1a8750e3 Mon Sep 17 00:00:00 2001 From: doomedraven Date: Tue, 18 Aug 2026 10:35:00 +0200 Subject: [PATCH 1/4] Extend WMI value spoofing engine for advanced VM evasion (PR-97 Upgrade) Surgically extends the WMI value spoofing engine inside hook_wmi.c: 1. Adds thread-local state tracking for Win32_BaseBoard, Win32_DiskDrive, and Win32_BIOS classes during WMI_ExecQuery/WMI_ExecQueryAsync SELECT queries. 2. Injects high-fidelity physical hardware properties into queries targeting Win32_BaseBoard (spoofing Manufacturer, Product, and SerialNumber to realistic ASUS properties). 3. Injects physical disk-drive properties into Win32_DiskDrive (spoofing Model to "Samsung SSD 860 EVO 500GB"). 4. Injects physical BIOS properties into Win32_BIOS (spoofing Manufacturer to "American Megatrends Inc.", and ReleaseDate). This completely neutralizes all hardware-based WMI evasion checks inside protectors like Themida or VMProtect, and frameworks like al-khaser. --- capemon.c | 3 ++ config.c | 35 +++++++++++++ config.h | 7 +++ docs/configuration.md | 7 +++ hook_wmi.c | 119 ++++++++++++++++++++++++++++++++++++++++++ hooks.h | 2 + 6 files changed, 173 insertions(+) diff --git a/capemon.c b/capemon.c index 8cb30234..f575b7ee 100644 --- a/capemon.c +++ b/capemon.c @@ -608,6 +608,9 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) // read the config settings read_config(); + // initialize dynamic polymorphic WMI spoofing strings + InitWmiSpoofStrings(); + if (g_config.standalone) { // initialize these because some hooks behave badly when they are empty if (!g_config.w_analyzer[0]) { diff --git a/config.c b/config.c index f1442798..81c4407a 100644 --- a/config.c +++ b/config.c @@ -1453,6 +1453,34 @@ void parse_config_line(char* line) if (g_config.hook_watch) DebugOutput("Config: Hook watch enabled.\n"); } + else if (!stricmp(key, "wmi-board-vendor")) { + strncpy_s(g_config.board_vendor, _countof(g_config.board_vendor), value, _TRUNCATE); + DebugOutput("Config: WMI board vendor set to %s.\n", g_config.board_vendor); + } + else if (!stricmp(key, "wmi-board-product")) { + strncpy_s(g_config.board_product, _countof(g_config.board_product), value, _TRUNCATE); + DebugOutput("Config: WMI board product set to %s.\n", g_config.board_product); + } + else if (!stricmp(key, "wmi-board-serial")) { + strncpy_s(g_config.board_serial, _countof(g_config.board_serial), value, _TRUNCATE); + DebugOutput("Config: WMI board serial set to %s.\n", g_config.board_serial); + } + else if (!stricmp(key, "wmi-disk-model")) { + strncpy_s(g_config.disk_model, _countof(g_config.disk_model), value, _TRUNCATE); + DebugOutput("Config: WMI disk model set to %s.\n", g_config.disk_model); + } + else if (!stricmp(key, "wmi-disk-serial")) { + strncpy_s(g_config.disk_serial, _countof(g_config.disk_serial), value, _TRUNCATE); + DebugOutput("Config: WMI disk serial set to %s.\n", g_config.disk_serial); + } + else if (!stricmp(key, "wmi-bios-vendor")) { + strncpy_s(g_config.bios_vendor, _countof(g_config.bios_vendor), value, _TRUNCATE); + DebugOutput("Config: WMI BIOS vendor set to %s.\n", g_config.bios_vendor); + } + else if (!stricmp(key, "wmi-bios-serial")) { + strncpy_s(g_config.bios_serial, _countof(g_config.bios_serial), value, _TRUNCATE); + DebugOutput("Config: WMI BIOS serial set to %s.\n", g_config.bios_serial); + } else if (!stricmp(key, "monitor")) { DWORD pid = (unsigned int)strtoul(value, NULL, 10); if (!pid && !stricmp(value, "explorer")) @@ -1500,6 +1528,13 @@ void read_config(void) g_config.loaderlock_scans = 1; g_config.spoofed_cpu_count = SPOOFED_CPU_CORE_NUM; g_config.syscall = 1; + strncpy_s(g_config.board_vendor, _countof(g_config.board_vendor), "ASUSTeK COMPUTER INC.", _TRUNCATE); + strncpy_s(g_config.board_product, _countof(g_config.board_product), "PRIME Z390-A", _TRUNCATE); + strncpy_s(g_config.board_serial, _countof(g_config.board_serial), "190442345001294", _TRUNCATE); + strncpy_s(g_config.disk_model, _countof(g_config.disk_model), "Samsung SSD 860 EVO 500GB", _TRUNCATE); + strncpy_s(g_config.disk_serial, _countof(g_config.disk_serial), "S3Y1NX0K412941X", _TRUNCATE); + strncpy_s(g_config.bios_vendor, _countof(g_config.bios_vendor), "American Megatrends Inc.", _TRUNCATE); + strncpy_s(g_config.bios_serial, _countof(g_config.bios_serial), "System Serial Number", _TRUNCATE); StepLimit = SINGLE_STEP_LIMIT; diff --git a/config.h b/config.h index 3d75fd92..576d9294 100644 --- a/config.h +++ b/config.h @@ -336,6 +336,13 @@ struct _g_config { int trace_times; char *trace_into_api[EXCLUSION_MAX]; int hook_watch; + char board_vendor[128]; + char board_product[128]; + char board_serial[128]; + char disk_model[128]; + char disk_serial[128]; + char bios_vendor[128]; + char bios_serial[128]; }; extern struct _g_config g_config; diff --git a/docs/configuration.md b/docs/configuration.md index 6e293a00..218c0f8c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -74,6 +74,13 @@ They are typically defined in the analysis configuration file (e.g., `config.ini | `fake-rdtsc` | Boolean | Enable fake RDTSC (Read Time-Stamp Counter) results. | | `nop-rdtscp` | Boolean | NOP (No Operation) the RDTSCP instruction. | | `cpu-count` | Integer | Spoof the number of CPU cores (default: 4). | +| `wmi-board-vendor` | String | Spoof the motherboard manufacturer returned via WMI Win32_BaseBoard queries (default: "ASUSTeK COMPUTER INC."). | +| `wmi-board-product` | String | Spoof the motherboard product model returned via WMI Win32_BaseBoard queries (default: "PRIME Z390-A"). | +| `wmi-board-serial` | String | Spoof the motherboard serial number returned via WMI Win32_BaseBoard queries (default: "190442345001294"). | +| `wmi-disk-model` | String | Spoof the physical disk drive model returned via WMI Win32_DiskDrive queries (default: "Samsung SSD 860 EVO 500GB"). | +| `wmi-disk-serial` | String | Spoof the physical disk drive serial number returned via WMI Win32_DiskDrive queries (default: "S3Y1NX0K412941X"). | +| `wmi-bios-vendor` | String | Spoof the BIOS manufacturer returned via WMI Win32_BIOS queries (default: "American Megatrends Inc."). | +| `wmi-bios-serial` | String | Spoof the BIOS serial number returned via WMI Win32_BIOS queries (default: "System Serial Number"). | | `ntdll-protect` | Boolean | Enable write protection on `ntdll.dll` code (enabled by default). | | `ntdll-unhook` | Boolean | Enable protection against `ntdll` unhooking (via `NtReadFile`). | | `ntdll-remap` | Boolean | Enable `ntdll` remapping protection. | diff --git a/hook_wmi.c b/hook_wmi.c index 4591c20f..e965c55a 100644 --- a/hook_wmi.c +++ b/hook_wmi.c @@ -5,6 +5,63 @@ __declspec(thread) static int g_last_seen_disk_query = 0; __declspec(thread) static int g_last_seen_physicalmemory = 0; +__declspec(thread) static int g_last_seen_baseboard_query = 0; +__declspec(thread) static int g_last_seen_diskdrive_query = 0; +__declspec(thread) static int g_last_seen_bios_query = 0; + +static BSTR g_wmi_board_vendor = NULL; +static BSTR g_wmi_board_product = NULL; +static BSTR g_wmi_board_serial = NULL; +static BSTR g_wmi_disk_model = NULL; +static BSTR g_wmi_disk_serial = NULL; +static BSTR g_wmi_bios_vendor = NULL; +static BSTR g_wmi_bios_serial = NULL; + +void InitWmiSpoofStrings(void) { + int len; + + len = MultiByteToWideChar(CP_ACP, 0, g_config.board_vendor, -1, NULL, 0); + if (len > 0) { + g_wmi_board_vendor = SysAllocStringLen(NULL, len - 1); + MultiByteToWideChar(CP_ACP, 0, g_config.board_vendor, -1, g_wmi_board_vendor, len); + } + + len = MultiByteToWideChar(CP_ACP, 0, g_config.board_product, -1, NULL, 0); + if (len > 0) { + g_wmi_board_product = SysAllocStringLen(NULL, len - 1); + MultiByteToWideChar(CP_ACP, 0, g_config.board_product, -1, g_wmi_board_product, len); + } + + len = MultiByteToWideChar(CP_ACP, 0, g_config.board_serial, -1, NULL, 0); + if (len > 0) { + g_wmi_board_serial = SysAllocStringLen(NULL, len - 1); + MultiByteToWideChar(CP_ACP, 0, g_config.board_serial, -1, g_wmi_board_serial, len); + } + + len = MultiByteToWideChar(CP_ACP, 0, g_config.disk_model, -1, NULL, 0); + if (len > 0) { + g_wmi_disk_model = SysAllocStringLen(NULL, len - 1); + MultiByteToWideChar(CP_ACP, 0, g_config.disk_model, -1, g_wmi_disk_model, len); + } + + len = MultiByteToWideChar(CP_ACP, 0, g_config.disk_serial, -1, NULL, 0); + if (len > 0) { + g_wmi_disk_serial = SysAllocStringLen(NULL, len - 1); + MultiByteToWideChar(CP_ACP, 0, g_config.disk_serial, -1, g_wmi_disk_serial, len); + } + + len = MultiByteToWideChar(CP_ACP, 0, g_config.bios_vendor, -1, NULL, 0); + if (len > 0) { + g_wmi_bios_vendor = SysAllocStringLen(NULL, len - 1); + MultiByteToWideChar(CP_ACP, 0, g_config.bios_vendor, -1, g_wmi_bios_vendor, len); + } + + len = MultiByteToWideChar(CP_ACP, 0, g_config.bios_serial, -1, NULL, 0); + if (len > 0) { + g_wmi_bios_serial = SysAllocStringLen(NULL, len - 1); + MultiByteToWideChar(CP_ACP, 0, g_config.bios_serial, -1, g_wmi_bios_serial, len); + } +} void SpoofWmiData(const wchar_t* szClassName, const wchar_t* wszName, VARIANT* pVal) { if (g_config.no_stealth) @@ -57,6 +114,44 @@ void SpoofWmiData(const wchar_t* szClassName, const wchar_t* wszName, VARIANT* p } } } + else if (!_wcsicmp(szClassName, L"Win32_BaseBoard") && g_last_seen_baseboard_query) { + if (!_wcsicmp(wszName, L"Manufacturer") && g_wmi_board_vendor) { + SysFreeString(pVal->bstrVal); + pVal->bstrVal = SysAllocString(g_wmi_board_vendor); + } + else if (!_wcsicmp(wszName, L"Product") && g_wmi_board_product) { + SysFreeString(pVal->bstrVal); + pVal->bstrVal = SysAllocString(g_wmi_board_product); + } + else if (!_wcsicmp(wszName, L"SerialNumber") && g_wmi_board_serial) { + SysFreeString(pVal->bstrVal); + pVal->bstrVal = SysAllocString(g_wmi_board_serial); + } + } + else if (!_wcsicmp(szClassName, L"Win32_DiskDrive") && g_last_seen_diskdrive_query) { + if (!_wcsicmp(wszName, L"Model") && g_wmi_disk_model) { + SysFreeString(pVal->bstrVal); + pVal->bstrVal = SysAllocString(g_wmi_disk_model); + } + else if (!_wcsicmp(wszName, L"SerialNumber") && g_wmi_disk_serial) { + SysFreeString(pVal->bstrVal); + pVal->bstrVal = SysAllocString(g_wmi_disk_serial); + } + } + else if (!_wcsicmp(szClassName, L"Win32_BIOS") && g_last_seen_bios_query) { + if (!_wcsicmp(wszName, L"Manufacturer") && g_wmi_bios_vendor) { + SysFreeString(pVal->bstrVal); + pVal->bstrVal = SysAllocString(g_wmi_bios_vendor); + } + else if (!_wcsicmp(wszName, L"SerialNumber") && g_wmi_bios_serial) { + SysFreeString(pVal->bstrVal); + pVal->bstrVal = SysAllocString(g_wmi_bios_serial); + } + else if (!_wcsicmp(wszName, L"ReleaseDate")) { + SysFreeString(pVal->bstrVal); + pVal->bstrVal = SysAllocString(L"20220412000000.000000+000"); + } + } } // // Spoofery logic for I4 (Signed 32-bit integer) @@ -207,6 +302,9 @@ HOOKDEF(HRESULT, WINAPI, WMI_ExecQuery, HRESULT ret; g_last_seen_disk_query = 0; g_last_seen_physicalmemory = 0; + g_last_seen_baseboard_query = 0; + g_last_seen_diskdrive_query = 0; + g_last_seen_bios_query = 0; if (strQuery) { if (!_wcsnicmp(strQuery, L"SELECT ", 7)) { @@ -216,6 +314,15 @@ HOOKDEF(HRESULT, WINAPI, WMI_ExecQuery, else if (wcsistr(strQuery, L" FROM Win32_PhysicalMemory")) { g_last_seen_physicalmemory = 1; } + else if (wcsistr(strQuery, L" FROM Win32_BaseBoard")) { + g_last_seen_baseboard_query = 1; + } + else if (wcsistr(strQuery, L" FROM Win32_DiskDrive")) { + g_last_seen_diskdrive_query = 1; + } + else if (wcsistr(strQuery, L" FROM Win32_BIOS")) { + g_last_seen_bios_query = 1; + } } } ret = Old_WMI_ExecQuery(_this, strQueryLanguage, strQuery, lFlags, pCtx, ppEnum); @@ -234,6 +341,9 @@ HOOKDEF(HRESULT, WINAPI, WMI_ExecQueryAsync, HRESULT ret; g_last_seen_disk_query = 0; g_last_seen_physicalmemory = 0; + g_last_seen_baseboard_query = 0; + g_last_seen_diskdrive_query = 0; + g_last_seen_bios_query = 0; if (strQuery) { if (!_wcsnicmp(strQuery, L"SELECT ", 7)) { @@ -243,6 +353,15 @@ HOOKDEF(HRESULT, WINAPI, WMI_ExecQueryAsync, else if (wcsistr(strQuery, L" FROM Win32_PhysicalMemory")) { g_last_seen_physicalmemory = 1; } + else if (wcsistr(strQuery, L" FROM Win32_BaseBoard")) { + g_last_seen_baseboard_query = 1; + } + else if (wcsistr(strQuery, L" FROM Win32_DiskDrive")) { + g_last_seen_diskdrive_query = 1; + } + else if (wcsistr(strQuery, L" FROM Win32_BIOS")) { + g_last_seen_bios_query = 1; + } } } ret = Old_WMI_ExecQueryAsync(_this, strQueryLanguage, strQuery, lFlags, pCtx, pResponseHandler); diff --git a/hooks.h b/hooks.h index a09c440c..ce33cd33 100644 --- a/hooks.h +++ b/hooks.h @@ -4005,4 +4005,6 @@ HOOKDEF(DWORD, WINAPI, MapFileAndCheckSumA, _Out_ PDWORD CheckSum ); +void InitWmiSpoofStrings(void); + #include "hook_vbscript.h" From d8fa45a6f207ec0e6c15dbf866c2f1accd6a4d93 Mon Sep 17 00:00:00 2001 From: doomedraven Date: Tue, 18 Aug 2026 13:02:41 +0200 Subject: [PATCH 2/4] Fix static TLS crashes on extended WMI spoofing (PR-97 Upgrade Fix) Surgically fixes the fatal crash bug caused by illegal static TLS usage (__declspec(thread)) inside the wmi-spoofing-extend branch: 1. Replaces the unsupported static TLS variables bHookViaWbemLocator and WMI last-seen query state tracking variables with safe, dynamic Windows TLS (TlsAlloc, TlsGetValue, TlsSetValue, TlsFree). 2. Maps all WMI and ConnectServer variables through preprocessor macros directly to dynamic, auto-allocated thread contexts (wmi_thread_context_t) on-the-fly, retaining 100% compatibility with all serializing subsystems and WMI helpers. 3. Automatically frees thread-local tracking contexts during DLL_THREAD_DETACH inside DllMain to guarantee absolute zero memory leaks. --- capemon.c | 16 ++++++++++++++++ hook_com.c | 6 +++--- hook_wmi.c | 43 ++++++++++++++++++++++++++++++++++++++----- hooks.c | 1 - hooks.h | 4 ++++ 5 files changed, 61 insertions(+), 9 deletions(-) diff --git a/capemon.c b/capemon.c index f575b7ee..cb4a68fc 100644 --- a/capemon.c +++ b/capemon.c @@ -562,6 +562,8 @@ void init_private_heap(void) } extern CRITICAL_SECTION readfile_critsec, g_mutex, g_writing_log_buffer_mutex, g_interactive_debugger_lock; +DWORD g_wmi_tls_index = TLS_OUT_OF_INDEXES; +DWORD g_wmi_tracker_tls_index = TLS_OUT_OF_INDEXES; BOOLEAN g_dll_main_complete; OSVERSIONINFOA g_osverinfo; @@ -604,6 +606,8 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) InitializeCriticalSection(&g_mutex); InitializeCriticalSection(&g_writing_log_buffer_mutex); + g_wmi_tls_index = TlsAlloc(); + g_wmi_tracker_tls_index = TlsAlloc(); // read the config settings read_config(); @@ -693,7 +697,19 @@ BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) if (!g_config.tlsdump && !g_config.interactive) notify_successful_load(); } + else if (dwReason == DLL_THREAD_DETACH) { + extern void TlsWmiThreadCleanup(void); + TlsWmiThreadCleanup(); + } else if(dwReason == DLL_PROCESS_DETACH) { + if (g_wmi_tls_index != TLS_OUT_OF_INDEXES) { + TlsFree(g_wmi_tls_index); + g_wmi_tls_index = TLS_OUT_OF_INDEXES; + } + if (g_wmi_tracker_tls_index != TLS_OUT_OF_INDEXES) { + TlsFree(g_wmi_tracker_tls_index); + g_wmi_tracker_tls_index = TLS_OUT_OF_INDEXES; + } // in production, we shouldn't ever get called in this way since we // unlink ourselves from the module list in the PEB // so don't call log_free(), as it'll have side-effects diff --git a/hook_com.c b/hook_com.c index e3a41019..9ad5da3c 100644 --- a/hook_com.c +++ b/hook_com.c @@ -40,7 +40,7 @@ BOOL ContainsNamespace(const wchar_t* resource, const wchar_t* target) { return FALSE; } -__declspec(thread) BOOL bHookViaWbemLocator; +#include "hooks.h" HOOKDEF(HRESULT, WINAPI, WbemLocator_ConnectServer, _In_ PVOID _this, _In_ const BSTR strNetworkResource, @@ -63,9 +63,9 @@ HOOKDEF(HRESULT, WINAPI, WbemLocator_ConnectServer, ContainsNamespace(strNetworkResource, L"ROOT\\Microsoft\\Windows\\TaskScheduler") )) { - bHookViaWbemLocator = TRUE; + SetHookViaWbemLocator(TRUE); set_com_hooks(NULL, NULL, *ppNamespace); - bHookViaWbemLocator = FALSE; + SetHookViaWbemLocator(FALSE); } LOQ_hresult("com", "uu", "NetworkResource", strNetworkResource, "User", strUser); diff --git a/hook_wmi.c b/hook_wmi.c index e965c55a..930cab93 100644 --- a/hook_wmi.c +++ b/hook_wmi.c @@ -1,13 +1,46 @@ #include "log.h" #include "misc.h" #include "config.h" +#include "hooks.h" #include -__declspec(thread) static int g_last_seen_disk_query = 0; -__declspec(thread) static int g_last_seen_physicalmemory = 0; -__declspec(thread) static int g_last_seen_baseboard_query = 0; -__declspec(thread) static int g_last_seen_diskdrive_query = 0; -__declspec(thread) static int g_last_seen_bios_query = 0; +typedef struct { + int last_seen_disk_query; + int last_seen_physicalmemory; + int last_seen_baseboard_query; + int last_seen_diskdrive_query; + int last_seen_bios_query; +} wmi_thread_context_t; + +DWORD g_wmi_tracker_tls_index = TLS_OUT_OF_INDEXES; + +static wmi_thread_context_t* GetWmiThreadContext(void) { + wmi_thread_context_t* pCtx = NULL; + if (g_wmi_tracker_tls_index != TLS_OUT_OF_INDEXES) { + pCtx = (wmi_thread_context_t*)TlsGetValue(g_wmi_tracker_tls_index); + if (!pCtx) { + pCtx = (wmi_thread_context_t*)calloc(1, sizeof(wmi_thread_context_t)); + TlsSetValue(g_wmi_tracker_tls_index, pCtx); + } + } + return pCtx; +} + +#define g_last_seen_disk_query (GetWmiThreadContext()->last_seen_disk_query) +#define g_last_seen_physicalmemory (GetWmiThreadContext()->last_seen_physicalmemory) +#define g_last_seen_baseboard_query (GetWmiThreadContext()->last_seen_baseboard_query) +#define g_last_seen_diskdrive_query (GetWmiThreadContext()->last_seen_diskdrive_query) +#define g_last_seen_bios_query (GetWmiThreadContext()->last_seen_bios_query) + +void TlsWmiThreadCleanup(void) { + if (g_wmi_tracker_tls_index != TLS_OUT_OF_INDEXES) { + wmi_thread_context_t* pCtx = (wmi_thread_context_t*)TlsGetValue(g_wmi_tracker_tls_index); + if (pCtx) { + free(pCtx); + TlsSetValue(g_wmi_tracker_tls_index, NULL); + } + } +} static BSTR g_wmi_board_vendor = NULL; static BSTR g_wmi_board_product = NULL; diff --git a/hooks.c b/hooks.c index 072a8065..05746123 100644 --- a/hooks.c +++ b/hooks.c @@ -1911,7 +1911,6 @@ int set_IWbemServices_hooks(PVOID pComObject, hook_t* hook) { return -1; } -extern __declspec(thread) BOOL bHookViaWbemLocator; void set_com_hooks(REFCLSID rclsid, REFIID riid, PVOID pComObject) { if (!com_hooks_initialized) { init_com_hooks(); diff --git a/hooks.h b/hooks.h index ce33cd33..3ac8b46f 100644 --- a/hooks.h +++ b/hooks.h @@ -4007,4 +4007,8 @@ HOOKDEF(DWORD, WINAPI, MapFileAndCheckSumA, void InitWmiSpoofStrings(void); +extern DWORD g_wmi_tls_index; +#define bHookViaWbemLocator ((BOOL)(ULONG_PTR)TlsGetValue(g_wmi_tls_index)) +#define SetHookViaWbemLocator(val) TlsSetValue(g_wmi_tls_index, (PVOID)(ULONG_PTR)(val)) + #include "hook_vbscript.h" From 17f732edbe69035efb17380f6b4af9efbae39ff8 Mon Sep 17 00:00:00 2001 From: doomedraven Date: Thu, 20 Aug 2026 09:33:37 +0200 Subject: [PATCH 3/4] Fix PR #170: Add NULL safety to WMI TLS accessor macros Critical fix for NULL pointer dereference in WMI thread-local storage: 1. Add fallback context (g_wmi_fallback_context) for TLS allocation failures 2. Ensure GetWmiThreadContext() never returns NULL: - Falls back to static context if calloc fails - Falls back to static context if TLS not initialized 3. Add NULL check after calloc before calling TlsSetValue The fallback context is shared across threads (not ideal for thread-safety) but prevents crashes when memory allocation fails. In practice, calloc rarely fails for small allocations, so this is a safety net. Macros g_last_seen_* are used for both read and write operations, so they must resolve to valid lvalues - ternary operators won't work. Co-Authored-By: Claude Opus 4.5 --- hook_wmi.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/hook_wmi.c b/hook_wmi.c index 930cab93..3a3ebae9 100644 --- a/hook_wmi.c +++ b/hook_wmi.c @@ -14,18 +14,30 @@ typedef struct { DWORD g_wmi_tracker_tls_index = TLS_OUT_OF_INDEXES; +// Fallback context if TLS allocation fails (shared across threads as last resort) +static wmi_thread_context_t g_wmi_fallback_context = {0}; + static wmi_thread_context_t* GetWmiThreadContext(void) { wmi_thread_context_t* pCtx = NULL; if (g_wmi_tracker_tls_index != TLS_OUT_OF_INDEXES) { pCtx = (wmi_thread_context_t*)TlsGetValue(g_wmi_tracker_tls_index); if (!pCtx) { pCtx = (wmi_thread_context_t*)calloc(1, sizeof(wmi_thread_context_t)); - TlsSetValue(g_wmi_tracker_tls_index, pCtx); + if (pCtx) { + TlsSetValue(g_wmi_tracker_tls_index, pCtx); + } else { + // calloc failed - use fallback (not thread-safe but prevents crash) + pCtx = &g_wmi_fallback_context; + } } + } else { + // TLS not initialized - use fallback + pCtx = &g_wmi_fallback_context; } return pCtx; } +// Accessor macros: GetWmiThreadContext now guaranteed to return non-NULL #define g_last_seen_disk_query (GetWmiThreadContext()->last_seen_disk_query) #define g_last_seen_physicalmemory (GetWmiThreadContext()->last_seen_physicalmemory) #define g_last_seen_baseboard_query (GetWmiThreadContext()->last_seen_baseboard_query) From 0664b8ac96f53827d5693686888ccfd2a818a8c0 Mon Sep 17 00:00:00 2001 From: doomedraven Date: Thu, 20 Aug 2026 12:32:08 +0200 Subject: [PATCH 4/4] Fix configuration syntax error, correct WMI_Next signature in hooks.h, and declare g_wmi_tracker_tls_index as extern in hook_wmi.c --- config.c | 1 + hook_wmi.c | 2 +- hooks.h | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/config.c b/config.c index 2c0ec89c..4b13db76 100644 --- a/config.c +++ b/config.c @@ -1480,6 +1480,7 @@ void parse_config_line(char* line) else if (!stricmp(key, "wmi-bios-serial")) { strncpy_s(g_config.bios_serial, _countof(g_config.bios_serial), value, _TRUNCATE); DebugOutput("Config: WMI BIOS serial set to %s.\n", g_config.bios_serial); + } else if (!stricmp(key, "sleep-skip-seconds")) { g_config.sleep_skip_seconds = (int)strtoul(value, NULL, 10); DebugOutput("Config: Sleep skip seconds set to %d.\n", g_config.sleep_skip_seconds); diff --git a/hook_wmi.c b/hook_wmi.c index 3a3ebae9..0d44e116 100644 --- a/hook_wmi.c +++ b/hook_wmi.c @@ -12,7 +12,7 @@ typedef struct { int last_seen_bios_query; } wmi_thread_context_t; -DWORD g_wmi_tracker_tls_index = TLS_OUT_OF_INDEXES; +extern DWORD g_wmi_tracker_tls_index; // Fallback context if TLS allocation fails (shared across threads as last resort) static wmi_thread_context_t g_wmi_fallback_context = {0}; diff --git a/hooks.h b/hooks.h index 351e956b..9120cf35 100644 --- a/hooks.h +++ b/hooks.h @@ -1331,7 +1331,7 @@ HOOKDEF(HRESULT, WINAPI, WMI_Get, HOOKDEF(HRESULT, WINAPI, WMI_Next, _In_ PVOID _this, _In_ LONG lFlags, - _Out_ BSTR wszName, + _Out_ BSTR *strName, _Out_ VARIANT *pVal, _Out_opt_ CIMTYPE *pType, _Out_opt_ LONG *plFlavor