Skip to content

Initial support for WMI value spoofing #97

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: capemon
Choose a base branch
from

Conversation

KillerInstinct
Copy link
Contributor

No description provided.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @KillerInstinct, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces initial support for WMI (Windows Management Instrumentation) value spoofing. The core functionality involves intercepting WMI queries and modifying the returned system property values, such as memory, disk, CPU, and GPU specifications. This capability is designed to present a manipulated system environment to applications, which can be useful for evasion techniques or influencing software behavior in specific contexts.

Highlights

  • WMI Value Spoofing Implementation: Introduced comprehensive logic within the WMI_Get hook to dynamically spoof various system properties reported via WMI. This includes system memory (TotalPhysicalMemory, TotalVisibleMemorySize, Capacity), disk size (Size), CPU core count (NumberOfCores), GPU RAM (AdapterRAM), and display refresh rate (MaxRefreshRate).
  • Context-Aware Spoofing: Implemented a mechanism in the WMI_ExecQuery hook to detect the type of WMI query being performed (e.g., for Win32_LogicalDisk or Win32_PhysicalMemory). This allows the WMI_Get hook to apply specific spoofing rules only when relevant, ensuring more precise and targeted value manipulation.
  • New Configuration Constants: Added new preprocessor definitions in config.h to support the WMI spoofing. This includes SPOOFED_GPU_RAM_WMI (an 'overflowed' value specifically for WMI's lVal type) and SPOOFED_REFRESH_RATE.
  • Error Handling and Logging Adjustments: Wrapped the WMI spoofing logic in WMI_Get with a __try/__except block for improved robustness. Minor adjustments were also made to WMI-related logging calls, including removing one from WMI_ExecMethodAsync and refining the output for empty object paths in WMI_GetObject.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces WMI value spoofing for various system properties like RAM, disk size, CPU cores, and GPU information. The changes are mainly in hook_wmi.c.

The implementation in New_WMI_Get looks reasonable, though there is some code duplication that could be refactored for better maintainability.

However, there is a critical issue in New_WMI_ExecQuery where the original function is not called. This will prevent the new spoofing logic for disk size and physical memory from working. My review includes a detailed explanation and a suggested fix for this.

Comment on lines 104 to 122
HRESULT ret = 0;
LOQ_hresult("system", "u", "Query", strQuery);

// Reset these on new query
g_last_seen_disk_query = 0;
g_last_seen_physicalmemory = 0;

if (!ret && !g_config.no_stealth && strQuery) {
if (!_wcsnicmp(strQuery, L"SELECT ", 7)) {
if (wcsistr(strQuery, L" FROM Win32_LogicalDisk")) {
g_last_seen_disk_query = 1;
//pipe("INFO:setting g_last_seen_disk_query");
}
else if (wcsistr(strQuery, L" FROM Win32_PhysicalMemory")) {
g_last_seen_physicalmemory = 1;
}
}
}
LOQ_hresult("system", "uu", "Query", strQuery, "QueryLanguage", strQueryLanguage);
return 0;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

The WMI_ExecQuery hook is implemented as a NOTAIL hook that always returns 0 without calling the original IWbemServices::ExecQuery function. This means that no WMI query will actually be executed, and no results will be returned.

As a consequence, the logic in New_WMI_Get that relies on the g_last_seen_disk_query and g_last_seen_physicalmemory flags will never be triggered, because WMI_Get is called on objects from the query result, which will be empty.

To fix this, WMI_ExecQuery must be changed to call the original function. This involves:

  1. Changing the hook definition in hooks.c from HOOK_NOTAIL(fastprox, WMI_ExecQuery, 6) to HOOK(fastprox, WMI_ExecQuery). I understand hooks.c is not in this PR, but this change is necessary for the feature to work.
  2. Updating hook_wmi.c to use HOOKDEF instead of HOOKDEF_NOTAIL and calling Old_WMI_ExecQuery.
  3. The check if (!ret && ...) is also redundant as ret is always 0 at that point.

I've provided a suggested implementation for the function body below. Note that this also requires changing HOOKDEF_NOTAIL(WINAPI, WMI_ExecQuery, ...) to HOOKDEF(HRESULT, WINAPI, WMI_ExecQuery, ...) in hook_wmi.c.

	HRESULT ret;

	// Reset these on new query
	g_last_seen_disk_query = 0;
	g_last_seen_physicalmemory = 0;

	if (!g_config.no_stealth && strQuery) {
		if (!_wcsnicmp(strQuery, L"SELECT ", 7)) {
			if (wcsistr(strQuery, L" FROM Win32_LogicalDisk")) {
				g_last_seen_disk_query = 1;
				//pipe("INFO:setting g_last_seen_disk_query");
			}
			else if (wcsistr(strQuery, L" FROM Win32_PhysicalMemory")) {
				g_last_seen_physicalmemory = 1;
			}
		}
	}

	ret = Old_WMI_ExecQuery(_this, strQueryLanguage, strQuery, lFlags, pCtx, ppEnum);

	LOQ_hresult("system", "uu", "Query", strQuery, "QueryLanguage", strQueryLanguage);
	return ret;

Comment on lines +26 to +68
if (pVal->vt == VT_BSTR) {
if (!wcsicmp(wszName, L"TotalPhysicalMemory")) {
unsigned long long actualMemory = wcstoull(pVal->bstrVal, NULL, 10);
if (actualMemory < SPOOFED_RAM) {
wchar_t wszMemory[16];
memset(wszMemory, 0x0, sizeof(wszMemory));
swprintf_s(wszMemory, sizeof(wszMemory), L"%llu", SPOOFED_RAM);
SysFreeString(pVal->bstrVal);
pVal->bstrVal = SysAllocString(wszMemory);
}
}
else if (!wcsicmp(wszName, L"TotalVisibleMemorySize")) {
unsigned long long actualMemory = wcstoull(pVal->bstrVal, NULL, 10);
// actualMemory is in Kilobytes, our spoofed values are in bytes
if (actualMemory < (SPOOFED_RAM / 1024)) {
wchar_t wszMemory[16];
memset(wszMemory, 0x0, sizeof(wszMemory));
swprintf_s(wszMemory, sizeof(wszMemory), L"%llu", (SPOOFED_RAM / 1024));
SysFreeString(pVal->bstrVal);
pVal->bstrVal = SysAllocString(wszMemory);
}
}
else if (g_last_seen_disk_query && !wcsicmp(wszName, L"Size")) {
unsigned long long lSize = wcstoull(pVal->bstrVal, NULL, 10);
if (lSize < SPOOFED_DISK_SIZE - RECOVERY_PARTITION_SIZE) {
wchar_t newSize[16];
memset(newSize, 0x0, sizeof(newSize));
swprintf_s(newSize, sizeof(newSize), L"%llu", SPOOFED_DISK_SIZE - RECOVERY_PARTITION_SIZE);
SysFreeString(pVal->bstrVal);
pVal->bstrVal = SysAllocString(newSize);
}
}
else if (g_last_seen_physicalmemory && !wcsicmp(wszName, L"Capacity")) {
unsigned long long actualMemory = wcstoull(pVal->bstrVal, NULL, 10);
if (actualMemory < SPOOFED_RAM) {
wchar_t wszMemory[16];
memset(wszMemory, 0x0, sizeof(wszMemory));
swprintf_s(wszMemory, sizeof(wszMemory), L"%llu", SPOOFED_RAM);
SysFreeString(pVal->bstrVal);
pVal->bstrVal = SysAllocString(wszMemory);
}
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

There is a lot of duplicated code for handling VT_BSTR spoofing. The logic for converting a spoofed value to a BSTR and replacing the existing one is repeated multiple times.

To improve maintainability and reduce redundancy, you could extract this logic into a helper function. For example:

static void spoof_bstr_value(VARIANT *pVal, unsigned long long spoofed_value_ull) {
    wchar_t buffer[21]; // Sufficient for a 64-bit unsigned long long
    swprintf_s(buffer, _countof(buffer), L"%llu", spoofed_value_ull);
    SysFreeString(pVal->bstrVal);
    pVal->bstrVal = SysAllocString(buffer);
}

Then you can simplify the code like this:

if (!wcsicmp(wszName, L"TotalPhysicalMemory")) {
    unsigned long long actualMemory = wcstoull(pVal->bstrVal, NULL, 10);
    if (actualMemory < SPOOFED_RAM) {
        spoof_bstr_value(pVal, SPOOFED_RAM);
    }
}

This would make the code much cleaner and easier to maintain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant