-
Notifications
You must be signed in to change notification settings - Fork 55
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
base: capemon
Are you sure you want to change the base?
Conversation
There was a problem hiding this 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., forWin32_LogicalDisk
orWin32_PhysicalMemory
). This allows theWMI_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 includesSPOOFED_GPU_RAM_WMI
(an 'overflowed' value specifically for WMI'slVal
type) andSPOOFED_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 fromWMI_ExecMethodAsync
and refining the output for empty object paths inWMI_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
-
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. ↩
There was a problem hiding this 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.
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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
- Changing the hook definition in
hooks.c
fromHOOK_NOTAIL(fastprox, WMI_ExecQuery, 6)
toHOOK(fastprox, WMI_ExecQuery)
. I understandhooks.c
is not in this PR, but this change is necessary for the feature to work. - Updating
hook_wmi.c
to useHOOKDEF
instead ofHOOKDEF_NOTAIL
and callingOld_WMI_ExecQuery
. - The check
if (!ret && ...)
is also redundant asret
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;
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); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
No description provided.