Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions capemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -604,10 +606,15 @@ 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();

// 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]) {
Expand Down Expand Up @@ -690,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
Expand Down
35 changes: 35 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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, "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);
Expand Down Expand Up @@ -1504,6 +1532,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);
g_config.sleep_skip_seconds = 10;

StepLimit = SINGLE_STEP_LIMIT;
Expand Down
7 changes: 7 additions & 0 deletions config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
int sleep_skip_seconds;
};

Expand Down
7 changes: 7 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,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. |
Expand Down
6 changes: 3 additions & 3 deletions hook_com.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
Expand Down
168 changes: 166 additions & 2 deletions hook_wmi.c
Original file line number Diff line number Diff line change
@@ -1,10 +1,112 @@
#include "log.h"
#include "misc.h"
#include "config.h"
#include "hooks.h"
#include <Wbemidl.h>

__declspec(thread) static int g_last_seen_disk_query = 0;
__declspec(thread) static int g_last_seen_physicalmemory = 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;

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};

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));
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)
#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;
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)
Expand Down Expand Up @@ -57,6 +159,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)
Expand Down Expand Up @@ -207,6 +347,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)) {
Expand All @@ -216,6 +359,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);
Expand All @@ -234,6 +386,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)) {
Expand All @@ -243,6 +398,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);
Expand Down
1 change: 0 additions & 1 deletion hooks.c
Original file line number Diff line number Diff line change
Expand Up @@ -1945,7 +1945,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();
Expand Down
8 changes: 7 additions & 1 deletion hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -4211,4 +4211,10 @@ HOOKDEF(DWORD, WINAPI, MapFileAndCheckSumA,
_Out_ PDWORD CheckSum
);

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"