From 96e7443b0722e9dececbdd04f3fb787216f280fa Mon Sep 17 00:00:00 2001 From: cdozdil Date: Thu, 5 Mar 2026 15:47:28 +0300 Subject: [PATCH] Added more keys/values to registry spoofing --- OptiScaler/hooks/Advapi32_Hooks.h | 513 +++++++++++++++++++++++------- 1 file changed, 392 insertions(+), 121 deletions(-) diff --git a/OptiScaler/hooks/Advapi32_Hooks.h b/OptiScaler/hooks/Advapi32_Hooks.h index f5501edc..5a8bb180 100644 --- a/OptiScaler/hooks/Advapi32_Hooks.h +++ b/OptiScaler/hooks/Advapi32_Hooks.h @@ -19,8 +19,6 @@ static PFN_RegQueryValueExA o_RegQueryValueExA = nullptr; static LSTATUS hkRegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) { - LSTATUS result = 0; - if (lpSubKey != nullptr && (wcscmp(L"SOFTWARE\\NVIDIA Corporation\\Global", lpSubKey) == 0 || wcscmp(L"SYSTEM\\ControlSet001\\Services\\nvlddmkm", lpSubKey) == 0)) { @@ -85,6 +83,262 @@ static LSTATUS hkRegCloseKey(HKEY hKey) return o_RegCloseKey(hKey); } +static std::wstring GetSpoofedProviderNameW() +{ + auto vendorId = Config::Instance()->SpoofedVendorId.value_or_default(); + if (vendorId == 0x10DE) + return L"NVIDIA"; + else if (vendorId == 0x1002) + return L"Advanced Micro Devices, Inc."; + else if (vendorId == 0x8086) + return L"Intel Corporation"; + + return L"NVIDIA"; +} + +static std::string GetSpoofedProviderNameA() +{ + auto vendorId = Config::Instance()->SpoofedVendorId.value_or_default(); + if (vendorId == 0x10DE) + return "NVIDIA"; + else if (vendorId == 0x1002) + return "Advanced Micro Devices, Inc."; + else if (vendorId == 0x8086) + return "Intel Corporation"; + + return "NVIDIA"; +} + +static LSTATUS SpoofRegSzW(LPBYTE lpData, LPDWORD lpcbData, LPDWORD lpType, const std::wstring& spoofedValue, + const char* logKey) +{ + size_t spoofedValueSize = (spoofedValue.size() + 1) * sizeof(wchar_t); + + if (lpData == nullptr || lpcbData == nullptr) + return ERROR_SUCCESS; + + if (*lpcbData >= spoofedValueSize) + { + std::memcpy(lpData, spoofedValue.c_str(), spoofedValueSize); + *lpcbData = static_cast(spoofedValueSize); + + if (lpType) + *lpType = REG_SZ; + + LOG_INFO("New {}: {}", logKey, wstring_to_string(spoofedValue)); + return ERROR_SUCCESS; + } + else + { + *lpcbData = static_cast(spoofedValueSize); + return ERROR_MORE_DATA; + } +} + +static LSTATUS SpoofRegSzA(LPBYTE lpData, LPDWORD lpcbData, LPDWORD lpType, const std::string& spoofedValue, + const char* logKey) +{ + size_t spoofedValueSize = (spoofedValue.size() + 1) * sizeof(char); + + if (lpData == nullptr || lpcbData == nullptr) + return ERROR_SUCCESS; + + if (*lpcbData >= spoofedValueSize) + { + std::memcpy(lpData, spoofedValue.c_str(), spoofedValueSize); + *lpcbData = static_cast(spoofedValueSize); + + if (lpType) + *lpType = REG_SZ; + + LOG_INFO("New {}: {}", logKey, spoofedValue); + return ERROR_SUCCESS; + } + else + { + *lpcbData = static_cast(spoofedValueSize); + return ERROR_MORE_DATA; + } +} + +// Replace vendor/device tokens in a single wide string segment. +// Handles VEN_XXXX, DEV_XXXX and SUBSYS_XXXXyyyy (vendor portion of subsystem). +static std::wstring ReplaceVendorDeviceTokensW(const std::wstring& input, const std::wstring& spoofedVendorId, + const std::wstring& spoofedDeviceId) +{ + std::wstring result = input; + size_t pos = 0; + + // Replace VEN_1002 + pos = 0; + while ((pos = result.find(L"VEN_1002", pos)) != std::wstring::npos) + { + result.replace(pos, 8, spoofedVendorId); + pos += spoofedVendorId.size(); + } + + // Replace VEN_8086 + pos = 0; + while ((pos = result.find(L"VEN_8086", pos)) != std::wstring::npos) + { + result.replace(pos, 8, spoofedVendorId); + pos += spoofedVendorId.size(); + } + + // Replace DEV_XXXX (4 hex digits = 8 chars total) + pos = 0; + while ((pos = result.find(L"DEV_", pos)) != std::wstring::npos) + { + if (pos + 8 <= result.size()) + { + result.replace(pos, 8, spoofedDeviceId); + pos += spoofedDeviceId.size(); + } + else + { + break; + } + } + + return result; +} + +// Replace vendor/device tokens in a single narrow string segment. +static std::string ReplaceVendorDeviceTokensA(const std::string& input, const std::string& spoofedVendorId, + const std::string& spoofedDeviceId) +{ + std::string result = input; + size_t pos = 0; + + pos = 0; + while ((pos = result.find("VEN_1002", pos)) != std::string::npos) + { + result.replace(pos, 8, spoofedVendorId); + pos += spoofedVendorId.size(); + } + + pos = 0; + while ((pos = result.find("VEN_8086", pos)) != std::string::npos) + { + result.replace(pos, 8, spoofedVendorId); + pos += spoofedVendorId.size(); + } + + pos = 0; + while ((pos = result.find("DEV_", pos)) != std::string::npos) + { + if (pos + 8 <= result.size()) + { + result.replace(pos, 8, spoofedDeviceId); + pos += spoofedDeviceId.size(); + } + else + { + break; + } + } + + return result; +} + +// Spoof a REG_MULTI_SZ buffer in-place by walking each null-terminated wide string +// segment and replacing vendor/device tokens. Writes back to lpData and updates lpcbData. +static void SpoofMultiSzW(LPBYTE lpData, LPDWORD lpcbData, const std::wstring& spoofedVendorId, + const std::wstring& spoofedDeviceId, const char* logKey) +{ + if (lpData == nullptr || lpcbData == nullptr || *lpcbData < sizeof(wchar_t)) + return; + + const DWORD bufferChars = *lpcbData / sizeof(wchar_t); + const wchar_t* src = reinterpret_cast(lpData); + + // Collect all segments + std::vector segments; + DWORD offset = 0; + while (offset < bufferChars) + { + if (src[offset] == L'\0') + { + offset++; + break; // double-null terminator + } + + std::wstring segment(src + offset); + segments.push_back(ReplaceVendorDeviceTokensW(segment, spoofedVendorId, spoofedDeviceId)); + offset += static_cast(segment.size()) + 1; + } + + if (segments.empty()) + return; + + // Rebuild the MULTI_SZ in-place (same buffer, same or smaller size — safe) + wchar_t* dst = reinterpret_cast(lpData); + DWORD written = 0; + for (const auto& seg : segments) + { + if (written + seg.size() + 1 < bufferChars) + { + std::wmemcpy(dst + written, seg.c_str(), seg.size() + 1); + written += static_cast(seg.size()) + 1; + } + } + + // Final double-null terminator + if (written < bufferChars) + dst[written] = L'\0'; + + *lpcbData = (written + 1) * sizeof(wchar_t); + + LOG_INFO("New {}: {}", logKey, wstring_to_string(segments[0])); +} + +// Spoof a REG_MULTI_SZ buffer in-place for the narrow (A) variant. +static void SpoofMultiSzA(LPBYTE lpData, LPDWORD lpcbData, const std::string& spoofedVendorId, + const std::string& spoofedDeviceId, const char* logKey) +{ + if (lpData == nullptr || lpcbData == nullptr || *lpcbData == 0) + return; + + const DWORD bufferBytes = *lpcbData; + const char* src = reinterpret_cast(lpData); + + std::vector segments; + DWORD offset = 0; + while (offset < bufferBytes) + { + if (src[offset] == '\0') + { + offset++; + break; + } + + std::string segment(src + offset); + segments.push_back(ReplaceVendorDeviceTokensA(segment, spoofedVendorId, spoofedDeviceId)); + offset += static_cast(segment.size()) + 1; + } + + if (segments.empty()) + return; + + char* dst = reinterpret_cast(lpData); + DWORD written = 0; + for (const auto& seg : segments) + { + if (written + seg.size() + 1 < bufferBytes) + { + std::memcpy(dst + written, seg.c_str(), seg.size() + 1); + written += static_cast(seg.size()) + 1; + } + } + + if (written < bufferBytes) + dst[written] = '\0'; + + *lpcbData = written + 1; + + LOG_INFO("New {}: {}", logKey, segments[0]); +} + // Original implementation: // https://github.com/artur-graniszewski/dlss-enabler-main/blob/1f8b24722f1b526ffb896ae62b6aa3ca766b0728/Utils/RegistryProxy.cpp#L137 static LONG hkRegQueryValueExW(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, @@ -95,130 +349,139 @@ static LONG hkRegQueryValueExW(HKEY hKey, LPCWSTR lpValueName, LPDWORD lpReserve std::wstring valueName = L""; if (lpValueName != NULL) - { valueName = std::wstring(lpValueName); - } if (Config::Instance()->SpoofHAGS.value_or_default() && valueName == L"HwSchMode") { - // Check if lpcbData is not NULL if (lpcbData != nullptr) { - // If lpData is NULL, we're being asked for the required size if (lpData == nullptr) { - *lpcbData = sizeof(DWORD); // Indicate the required size - - // Set the type to REG_DWORD + *lpcbData = sizeof(DWORD); if (lpType) - { *lpType = REG_DWORD; - } return ERROR_SUCCESS; } - // Check if the buffer is large enough if (*lpcbData >= sizeof(DWORD)) { *(DWORD*) lpData = 2; - - // Set the type to REG_DWORD if (lpType) - { *lpType = REG_DWORD; - } - - // Set the size of the data returned *lpcbData = sizeof(DWORD); - - // Return success return ERROR_SUCCESS; } else { - // Buffer is too small, return required size *lpcbData = sizeof(DWORD); return ERROR_MORE_DATA; } } - // If lpcbData is NULL, return an error return ERROR_INVALID_PARAMETER; } - // Call the original function auto result = o_RegQueryValueExW(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData); - // Check the result of the query and if the valueName matches if (result == ERROR_SUCCESS && Config::Instance()->SpoofRegistry.value_or_default()) { if (valueName == L"DriverVersion") { const std::wstring spoofedValue = Config::Instance()->SpoofedDriver.value_or_default(); - - size_t spoofedValueSize = - (spoofedValue.size() + 1) * sizeof(wchar_t); // Size in bytes including null terminator + size_t spoofedValueSize = (spoofedValue.size() + 1) * sizeof(wchar_t); if (lpData != nullptr && lpcbData != nullptr) { - // Check if buffer size is sufficient if (*lpcbData >= spoofedValueSize) { - // Copy the spoofed value into lpData std::memcpy(lpData, spoofedValue.c_str(), spoofedValueSize); - // Update lpcbData with the size of the spoofed value *lpcbData = static_cast(spoofedValueSize); - LOG_INFO("New DriverVersion: {}", wstring_to_string(spoofedValue)); } else { - // If buffer is too small, set lpcbData to the required size *lpcbData = static_cast(spoofedValueSize); - result = ERROR_MORE_DATA; // Indicate that buffer was too small + result = ERROR_MORE_DATA; } } } - if (valueName == L"HardwareID") + if (valueName == L"DriverDesc") { - // Handle REG_SZ type - std::wstring data(reinterpret_cast(lpData), *lpcbData / sizeof(wchar_t)); - std::wstring newData = data; - size_t pos = 0; - bool found = false; + const std::wstring spoofedValue = Config::Instance()->SpoofedGPUName.value_or_default(); + auto spoofResult = SpoofRegSzW(lpData, lpcbData, lpType, spoofedValue, "DriverDesc"); + if (spoofResult != ERROR_SUCCESS) + result = spoofResult; + } - // Replace VEN_1002 and VEN_8086 with VEN_10DE in REG_SZ - while ((pos = newData.find(L"VEN_1002", pos)) != std::wstring::npos) + if (valueName == L"ProviderName") + { + const std::wstring spoofedValue = GetSpoofedProviderNameW(); + auto spoofResult = SpoofRegSzW(lpData, lpcbData, lpType, spoofedValue, "ProviderName"); + if (spoofResult != ERROR_SUCCESS) + result = spoofResult; + } + + if (valueName == L"HardwareInformation.AdapterString") + { + const std::wstring spoofedValue = Config::Instance()->SpoofedGPUName.value_or_default(); + auto spoofResult = SpoofRegSzW(lpData, lpcbData, lpType, spoofedValue, "HardwareInformation.AdapterString"); + if (spoofResult != ERROR_SUCCESS) + result = spoofResult; + } + + if ((valueName == L"HardwareID" || valueName == L"MatchingDeviceId") && lpData != nullptr && + lpcbData != nullptr) + { + DWORD regType = lpType ? *lpType : REG_NONE; + + if (regType == REG_MULTI_SZ) { - newData.replace(pos, 8, vendorId); - pos += 8; // Move past the replacement - found = true; + SpoofMultiSzW(lpData, lpcbData, vendorId, deviceId, + valueName == L"HardwareID" ? "HardwareID" : "MatchingDeviceId"); } - - pos = 0; - while ((pos = newData.find(L"VEN_8086", pos)) != std::wstring::npos) + else { - newData.replace(pos, 8, vendorId); - pos += 8; // Move past the replacement - found = true; - } + // REG_SZ fallback — single string, replace in-place + std::wstring data(reinterpret_cast(lpData), *lpcbData / sizeof(wchar_t)); + std::wstring newData = ReplaceVendorDeviceTokensW(data, vendorId, deviceId); - if (found) - { - // Replace Device Id - pos = 0; - while ((pos = newData.find(L"DEV_", pos)) != std::wstring::npos) + if (newData != data) { - newData.replace(pos, 8, deviceId); - pos += 8; // Move past the replacement + size_t newSize = (newData.size() + 1) * sizeof(wchar_t); + if (*lpcbData >= newSize) + { + std::memcpy(lpData, newData.c_str(), newSize); + *lpcbData = static_cast(newSize); + LOG_INFO("New {}: {}", valueName == L"HardwareID" ? "HardwareID" : "MatchingDeviceId", + wstring_to_string(newData)); + } } - - LOG_INFO("New HardwareID: {}", wstring_to_string(newData)); } + } - // Copy the new data back - wcscpy_s(reinterpret_cast(lpData), *lpcbData / sizeof(wchar_t), newData.c_str()); + // Intercept \Device\VideoN values from HARDWARE\DEVICEMAP\VIDEO + // These return the full PCI registry path (e.g. \REGISTRY\Machine\SYSTEM\...\VEN_1002&DEV_...) + // which games use to directly open the adapter's Enum\PCI key. + if (lpData != nullptr && lpcbData != nullptr && *lpcbData >= sizeof(wchar_t) && valueName.size() >= 13 && + _wcsnicmp(valueName.c_str(), L"\\Device\\Video", 13) == 0) + { + DWORD regType = lpType ? *lpType : REG_NONE; + if (regType == REG_SZ || regType == REG_EXPAND_SZ) + { + std::wstring path(reinterpret_cast(lpData), *lpcbData / sizeof(wchar_t)); + std::wstring newPath = ReplaceVendorDeviceTokensW(path, vendorId, deviceId); + if (newPath != path) + { + size_t newSize = (newPath.size() + 1) * sizeof(wchar_t); + if (*lpcbData >= newSize) + { + std::memcpy(lpData, newPath.c_str(), newSize); + *lpcbData = static_cast(newSize); + LOG_INFO("New VideoDeviceMap path: {}", wstring_to_string(newPath)); + } + } + } } } @@ -233,129 +496,137 @@ LONG WINAPI hkRegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved std::string valueName = ""; if (lpValueName != NULL) - { valueName = std::string(lpValueName); - } if (Config::Instance()->SpoofHAGS.value_or_default() && valueName == "HwSchMode") { - // Check if lpcbData is not NULL if (lpcbData != nullptr) { - // If lpData is NULL, we're being asked for the required size if (lpData == nullptr) { - *lpcbData = sizeof(DWORD); // Indicate the required size - - // Set the type to REG_DWORD + *lpcbData = sizeof(DWORD); if (lpType) - { *lpType = REG_DWORD; - } return ERROR_SUCCESS; } - // Check if the buffer is large enough if (*lpcbData >= sizeof(DWORD)) { *(DWORD*) lpData = 2; - - // Set the type to REG_DWORD if (lpType) - { *lpType = REG_DWORD; - } - - // Set the size of the data returned *lpcbData = sizeof(DWORD); - - // Return success return ERROR_SUCCESS; } else { - // Buffer is too small, return required size *lpcbData = sizeof(DWORD); return ERROR_MORE_DATA; } } - // If lpcbData is NULL, return an error return ERROR_INVALID_PARAMETER; } - // Call the original function auto result = o_RegQueryValueExA(hKey, lpValueName, lpReserved, lpType, lpData, lpcbData); - // Check the result of the query and if the valueName matches if (result == ERROR_SUCCESS && Config::Instance()->SpoofRegistry.value_or_default()) { if (valueName == "DriverVersion") { const std::string spoofedValue = wstring_to_string(Config::Instance()->SpoofedDriver.value_or_default()); - size_t spoofedValueSize = - (spoofedValue.size() + 1) * sizeof(wchar_t); // Size in bytes including null terminator + size_t spoofedValueSize = (spoofedValue.size() + 1) * sizeof(char); if (lpData != nullptr && lpcbData != nullptr) { - // Check if buffer size is sufficient if (*lpcbData >= spoofedValueSize) { - // Copy the spoofed value into lpData std::memcpy(lpData, spoofedValue.c_str(), spoofedValueSize); - // Update lpcbData with the size of the spoofed value *lpcbData = static_cast(spoofedValueSize); - LOG_INFO("New DriverVersion: {}", spoofedValue); } else { - // If buffer is too small, set lpcbData to the required size *lpcbData = static_cast(spoofedValueSize); - result = ERROR_MORE_DATA; // Indicate that buffer was too small + result = ERROR_MORE_DATA; } } } - if (valueName == "HardwareID") + if (valueName == "DriverDesc") { - // Handle REG_SZ type - std::string data(reinterpret_cast(lpData), *lpcbData / sizeof(char)); - std::string newData = data; - size_t pos = 0; - bool found = false; + const std::string spoofedValue = wstring_to_string(Config::Instance()->SpoofedGPUName.value_or_default()); + auto spoofResult = SpoofRegSzA(lpData, lpcbData, lpType, spoofedValue, "DriverDesc"); + if (spoofResult != ERROR_SUCCESS) + result = spoofResult; + } - // Replace VEN_1002 and VEN_8086 with VEN_10DE in REG_SZ - while ((pos = newData.find("VEN_1002", pos)) != std::wstring::npos) + if (valueName == "ProviderName") + { + const std::string spoofedValue = GetSpoofedProviderNameA(); + auto spoofResult = SpoofRegSzA(lpData, lpcbData, lpType, spoofedValue, "ProviderName"); + if (spoofResult != ERROR_SUCCESS) + result = spoofResult; + } + + if (valueName == "HardwareInformation.AdapterString") + { + const std::string spoofedValue = wstring_to_string(Config::Instance()->SpoofedGPUName.value_or_default()); + auto spoofResult = SpoofRegSzA(lpData, lpcbData, lpType, spoofedValue, "HardwareInformation.AdapterString"); + if (spoofResult != ERROR_SUCCESS) + result = spoofResult; + } + + if ((valueName == "HardwareID" || valueName == "MatchingDeviceId") && lpData != nullptr && lpcbData != nullptr) + { + DWORD regType = lpType ? *lpType : REG_NONE; + + if (regType == REG_MULTI_SZ) { - newData.replace(pos, 8, vendorId); - pos += 8; // Move past the replacement - found = true; + SpoofMultiSzA(lpData, lpcbData, vendorId, deviceId, + valueName == "HardwareID" ? "HardwareID" : "MatchingDeviceId"); } - - pos = 0; - while ((pos = newData.find("VEN_8086", pos)) != std::wstring::npos) + else { - newData.replace(pos, 8, vendorId); - pos += 8; // Move past the replacement - found = true; - } + // REG_SZ fallback + std::string data(reinterpret_cast(lpData), *lpcbData); + std::string newData = ReplaceVendorDeviceTokensA(data, vendorId, deviceId); - // Replace Device Id - if (found) - { - pos = 0; - while ((pos = newData.find("DEV_", pos)) != std::string::npos) + if (newData != data) { - newData.replace(pos, 8, deviceId); - pos += 8; // Move past the replacement + size_t newSize = newData.size() + 1; + if (*lpcbData >= newSize) + { + std::memcpy(lpData, newData.c_str(), newSize); + *lpcbData = static_cast(newSize); + LOG_INFO("New {}: {}", valueName == "HardwareID" ? "HardwareID" : "MatchingDeviceId", newData); + } } - - LOG_INFO("New HardwareID: {}", newData); } + } - // Copy the new data back - std::memcpy(lpData, newData.c_str(), *lpcbData); + // Intercept \Device\VideoN values from HARDWARE\DEVICEMAP\VIDEO + // These return the full PCI registry path (e.g. \REGISTRY\Machine\SYSTEM\...\VEN_1002&DEV_...) + // which games use to directly open the adapter's Enum\PCI key. + if (lpData != nullptr && lpcbData != nullptr && *lpcbData >= sizeof(char) && valueName.size() >= 13 && + _strnicmp(valueName.c_str(), "\\Device\\Video", 13) == 0) + { + DWORD regType = lpType ? *lpType : REG_NONE; + if (regType == REG_SZ || regType == REG_EXPAND_SZ) + { + std::string path(reinterpret_cast(lpData), *lpcbData); + std::string newPath = ReplaceVendorDeviceTokensA(path, vendorId, deviceId); + if (newPath != path) + { + size_t newSize = newPath.size() + 1; + if (*lpcbData >= newSize) + { + std::memcpy(lpData, newPath.c_str(), newSize); + *lpcbData = static_cast(newSize); + LOG_INFO("New VideoDeviceMap path: {}", newPath); + } + } + } } }