523 lines
16 KiB
C++
523 lines
16 KiB
C++
|
/*
|
||
|
|
||
|
Copyright (c) 2000 Microsoft Corporation
|
||
|
|
||
|
Module Name:
|
||
|
|
||
|
RegQueryValueEx.cpp
|
||
|
|
||
|
Abstract:
|
||
|
|
||
|
This DLL hooks RegQueryValueExA so that we can return the "all-users" location
|
||
|
for the StartMenu, Desktop and Startup folders instead of the per-user location.
|
||
|
|
||
|
We also hook RegCreateKeyA/RegCreateKeyExA to make people who add entries to the
|
||
|
HKCU "run" and "Uninstall" keys really add them to HKLM.
|
||
|
|
||
|
Notes:
|
||
|
|
||
|
History:
|
||
|
|
||
|
08/07/2000 reinerf Created
|
||
|
02/27/2001 robkenny Converted to use CString
|
||
|
|
||
|
*/
|
||
|
|
||
|
#include "precomp.h"
|
||
|
|
||
|
// This module has been given an official blessing to use the str routines.
|
||
|
#include "LegalStr.h"
|
||
|
|
||
|
IMPLEMENT_SHIM_BEGIN(ProfilesRegQueryValueEx)
|
||
|
#include "ShimHookMacro.h"
|
||
|
|
||
|
#include <shlwapi.h> // for StrRStrIA
|
||
|
|
||
|
APIHOOK_ENUM_BEGIN
|
||
|
APIHOOK_ENUM_ENTRY(RegQueryValueExA)
|
||
|
APIHOOK_ENUM_ENTRY(RegCreateKeyA)
|
||
|
APIHOOK_ENUM_ENTRY(RegCreateKeyExA)
|
||
|
APIHOOK_ENUM_ENTRY(RegOpenKeyA)
|
||
|
APIHOOK_ENUM_ENTRY(RegOpenKeyExA)
|
||
|
APIHOOK_ENUM_END
|
||
|
|
||
|
|
||
|
#ifndef MAX
|
||
|
#define MAX(x,y) (((x) > (y)) ? (x) : (y))
|
||
|
#endif
|
||
|
|
||
|
#ifndef ARRAYSIZE
|
||
|
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
|
||
|
#endif
|
||
|
|
||
|
LPCSTR g_aBadKeys[] =
|
||
|
{
|
||
|
{"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"},
|
||
|
{"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall"},
|
||
|
};
|
||
|
|
||
|
LPCSTR g_aBadShellFolderKeys[] =
|
||
|
{
|
||
|
{"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"},
|
||
|
{"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\User Shell Folders"},
|
||
|
};
|
||
|
|
||
|
|
||
|
// given an hkey, call NtQueryObject to lookup its name.
|
||
|
// returns strings in the format: "\REGISTRY\USER\S-1-5-xxxxx\Software\Microsoft\Windows\CurrentVersion"
|
||
|
BOOL GetKeyName(HKEY hk, LPSTR pszName, DWORD cchName)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
ULONG cbSize = 0;
|
||
|
|
||
|
// get the size needed for the name buffer
|
||
|
NtQueryObject(hk, ObjectNameInformation, NULL, 0, &cbSize);
|
||
|
|
||
|
if (cbSize)
|
||
|
{
|
||
|
POBJECT_NAME_INFORMATION pNameInfo = (POBJECT_NAME_INFORMATION)LocalAlloc(LPTR, cbSize);
|
||
|
|
||
|
if (pNameInfo)
|
||
|
{
|
||
|
NTSTATUS status = NtQueryObject(hk, ObjectNameInformation, (void*)pNameInfo, cbSize, NULL);
|
||
|
|
||
|
if (NT_SUCCESS(status) && WideCharToMultiByte(CP_ACP, 0, pNameInfo->Name.Buffer, -1, pszName, cchName, NULL, NULL))
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
|
||
|
LocalFree(pNameInfo);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
// If hk points underneath HKCU and matches pszSearchName, then return TRUE
|
||
|
BOOL DoesKeyMatch(HKEY hk, LPCSTR pszSearchName)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
// make sure it is not one of the pre-defined keys (eg HKEY_LOCAL_MACHINE)
|
||
|
if (!((LONG)((ULONG_PTR)hk) & 0x80000000))
|
||
|
{
|
||
|
CHAR szKeyName[MAX_PATH * 2]; // should be big enought to hold any registry key path
|
||
|
|
||
|
if (GetKeyName(hk, szKeyName, ARRAYSIZE(szKeyName)))
|
||
|
{
|
||
|
// is the key under HKCU ?
|
||
|
if (StrCmpNIA(szKeyName, "\\REGISTRY\\USER\\", ARRAYSIZE("\\REGISTRY\\USER\\")-1) == 0)
|
||
|
{
|
||
|
LPSTR psz = StrRStrIA(szKeyName, NULL, pszSearchName);
|
||
|
|
||
|
if (psz && (lstrlenA(psz) == lstrlenA(pszSearchName)))
|
||
|
{
|
||
|
// we found a substring and its the same length, so our hkey matches the search!
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL IsBadHKCUKey(HKEY hk, LPCSTR* ppszNewKey)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
int i;
|
||
|
|
||
|
for (i=0; i < ARRAYSIZE(g_aBadKeys); i++)
|
||
|
{
|
||
|
if (DoesKeyMatch(hk, g_aBadKeys[i]))
|
||
|
{
|
||
|
*ppszNewKey = g_aBadKeys[i];
|
||
|
bRet = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL IsBadShellFolderKey(HKEY hk, LPCSTR* ppszNewKey)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
int i;
|
||
|
|
||
|
for (i=0; i < ARRAYSIZE(g_aBadShellFolderKeys); i++)
|
||
|
{
|
||
|
if (DoesKeyMatch(hk, g_aBadShellFolderKeys[i]))
|
||
|
{
|
||
|
*ppszNewKey = g_aBadShellFolderKeys[i];
|
||
|
bRet = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegCreateKeyA)(
|
||
|
HKEY hKey,
|
||
|
LPCSTR pszSubKey,
|
||
|
PHKEY phkResult
|
||
|
)
|
||
|
{
|
||
|
LPCSTR pszNewHKLMKey;
|
||
|
LONG lRet = ORIGINAL_API(RegCreateKeyA)(hKey, pszSubKey, phkResult);
|
||
|
|
||
|
if ((lRet == ERROR_SUCCESS) &&
|
||
|
IsBadHKCUKey(*phkResult, &pszNewHKLMKey))
|
||
|
{
|
||
|
// its a bad HKCU key-- redirect to HKLM
|
||
|
RegCloseKey(*phkResult);
|
||
|
|
||
|
lRet = ORIGINAL_API(RegCreateKeyA)(HKEY_LOCAL_MACHINE, pszNewHKLMKey, phkResult);
|
||
|
|
||
|
LOGN(eDbgLevelInfo, "[RegCreateKeyA] overriding \"%s\" create key request w/ HKLM value (return = %d)", pszSubKey, lRet);
|
||
|
}
|
||
|
|
||
|
return lRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegCreateKeyExA)(
|
||
|
HKEY hKey,
|
||
|
LPCSTR pszSubKey,
|
||
|
DWORD Reserved,
|
||
|
LPSTR lpClass,
|
||
|
DWORD dwOptions,
|
||
|
REGSAM samDesired,
|
||
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
||
|
PHKEY phkResult,
|
||
|
LPDWORD lpdwDisposition
|
||
|
)
|
||
|
{
|
||
|
LPCSTR pszNewHKLMKey;
|
||
|
LONG lRet = ORIGINAL_API(RegCreateKeyExA)(hKey,
|
||
|
pszSubKey,
|
||
|
Reserved,
|
||
|
lpClass,
|
||
|
dwOptions,
|
||
|
samDesired,
|
||
|
lpSecurityAttributes,
|
||
|
phkResult,
|
||
|
lpdwDisposition);
|
||
|
|
||
|
if ((lRet == ERROR_SUCCESS) &&
|
||
|
IsBadHKCUKey(*phkResult, &pszNewHKLMKey))
|
||
|
{
|
||
|
// its a bad HCKU key-- redirect to HKLM
|
||
|
RegCloseKey(*phkResult);
|
||
|
|
||
|
lRet = ORIGINAL_API(RegCreateKeyExA)(HKEY_LOCAL_MACHINE,
|
||
|
pszNewHKLMKey,
|
||
|
Reserved,
|
||
|
lpClass,
|
||
|
dwOptions,
|
||
|
samDesired,
|
||
|
lpSecurityAttributes,
|
||
|
phkResult,
|
||
|
lpdwDisposition);
|
||
|
|
||
|
LOGN(eDbgLevelInfo, "[RegCreateKeyExA] overriding \"%s\" create key request w/ HKLM value (return = %d)", pszSubKey, lRet);
|
||
|
}
|
||
|
|
||
|
return lRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegOpenKeyA)(
|
||
|
HKEY hKey,
|
||
|
LPCSTR pszSubKey,
|
||
|
PHKEY phkResult
|
||
|
)
|
||
|
{
|
||
|
LPCSTR pszNewHKLMKey;
|
||
|
LONG lRet = ORIGINAL_API(RegOpenKeyA)(hKey, pszSubKey, phkResult);
|
||
|
|
||
|
if ((lRet == ERROR_SUCCESS) &&
|
||
|
IsBadHKCUKey(*phkResult, &pszNewHKLMKey))
|
||
|
{
|
||
|
// its a bad HCKU key-- redirect to HKLM
|
||
|
RegCloseKey(*phkResult);
|
||
|
|
||
|
lRet = ORIGINAL_API(RegOpenKeyA)(HKEY_LOCAL_MACHINE, pszNewHKLMKey, phkResult);
|
||
|
|
||
|
LOGN(eDbgLevelInfo, "[RegOpenKeyA] overriding \"%s\" create key request w/ HKLM value (return = %d)", pszSubKey, lRet);
|
||
|
}
|
||
|
|
||
|
return lRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegOpenKeyExA)(
|
||
|
HKEY hKey,
|
||
|
LPCSTR pszSubKey,
|
||
|
DWORD ulOptions,
|
||
|
REGSAM samDesired,
|
||
|
PHKEY phkResult
|
||
|
)
|
||
|
{
|
||
|
LPCSTR pszNewHKLMKey;
|
||
|
LONG lRet = ORIGINAL_API(RegOpenKeyExA)(hKey, pszSubKey, ulOptions, samDesired, phkResult);
|
||
|
|
||
|
if ((lRet == ERROR_SUCCESS) &&
|
||
|
IsBadHKCUKey(*phkResult, &pszNewHKLMKey))
|
||
|
{
|
||
|
// its a bad HCKU key-- redirect to HKLM
|
||
|
RegCloseKey(*phkResult);
|
||
|
|
||
|
lRet = ORIGINAL_API(RegOpenKeyExA)(HKEY_LOCAL_MACHINE, pszNewHKLMKey, ulOptions, samDesired, phkResult);
|
||
|
|
||
|
LOGN(eDbgLevelInfo, "[RegOpenKeyExA] overriding \"%s\" create key request w/ HKLM value (return = %d)", pszSubKey, lRet);
|
||
|
}
|
||
|
|
||
|
return lRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
LONG
|
||
|
GetAllUsersRegValueA(
|
||
|
LPSTR szRegValue,
|
||
|
DWORD cbOriginal,
|
||
|
DWORD* pcbData,
|
||
|
int nFolder,
|
||
|
int nFolderCommon
|
||
|
)
|
||
|
{
|
||
|
LONG lRet = ERROR_SUCCESS;
|
||
|
|
||
|
if (!szRegValue)
|
||
|
{
|
||
|
// if the caller is querying for the necessary size, return the "worst case" since we don't know if
|
||
|
// we are going to have to lie or not
|
||
|
*pcbData = MAX_PATH;
|
||
|
}
|
||
|
else if (szRegValue[0] != '\0')
|
||
|
{
|
||
|
CHAR szPath[MAX_PATH];
|
||
|
|
||
|
if (SUCCEEDED(SHGetFolderPathA(NULL, nFolder, NULL, SHGFP_TYPE_CURRENT, szPath))) {
|
||
|
CHAR szShortRegPath[MAX_PATH];
|
||
|
CHAR szShortGSFPath[MAX_PATH];
|
||
|
BOOL bUseLFN;
|
||
|
BOOL bSame = FALSE;
|
||
|
|
||
|
if (lstrcmpiA(szPath, szRegValue) == 0) {
|
||
|
bSame = TRUE;
|
||
|
bUseLFN = TRUE;
|
||
|
} else if (GetShortPathNameA(szPath, szShortGSFPath, ARRAYSIZE(szShortGSFPath)) &&
|
||
|
GetShortPathNameA(szRegValue, szShortRegPath, ARRAYSIZE(szShortRegPath)) &&
|
||
|
(lstrcmpiA(szShortGSFPath, szShortRegPath) == 0)) {
|
||
|
bSame = TRUE;
|
||
|
|
||
|
//
|
||
|
// Since the sfn was returned, use that to copy over the output buffer.
|
||
|
//
|
||
|
bUseLFN = FALSE;
|
||
|
}
|
||
|
|
||
|
if (bSame && SUCCEEDED(SHGetFolderPathA(NULL, nFolderCommon, NULL, SHGFP_TYPE_CURRENT, szPath))) {
|
||
|
if (bUseLFN) {
|
||
|
if ((lstrlenA(szPath) + 1) <= (int)cbOriginal) {
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[GetAllUsersRegValueA] overriding per-user reg value w/ common value");
|
||
|
lstrcpyA(szRegValue, szPath);
|
||
|
} else {
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[GetAllUsersRegValueA] returning ERROR_MORE_DATA for special folder value");
|
||
|
lRet = ERROR_MORE_DATA;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Either we used this much room, or this is how much we need to have.
|
||
|
//
|
||
|
*pcbData = lstrlenA(szPath) + 1;
|
||
|
|
||
|
} else if (GetShortPathNameA(szPath, szShortGSFPath, ARRAYSIZE(szShortGSFPath))) {
|
||
|
|
||
|
if ((lstrlenA(szShortGSFPath) + 1) <= (int)cbOriginal) {
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[GetAllUsersRegValueA] overriding per-user reg value w/ common value");
|
||
|
|
||
|
lstrcpyA(szRegValue, szShortGSFPath);
|
||
|
} else {
|
||
|
LOGN(
|
||
|
eDbgLevelInfo,
|
||
|
"[GetAllUsersRegValueA] returning ERROR_MORE_DATA for special folder value");
|
||
|
lRet = ERROR_MORE_DATA;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Either we used this much room, or this is how much we need to have.
|
||
|
//
|
||
|
*pcbData = lstrlenA(szShortGSFPath) + 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return lRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// If the app is asking for the per-user "Desktop", "Start Menu" or "Startup" values by
|
||
|
// groveling the registry, then redirect it to the proper per-machine values.
|
||
|
//
|
||
|
LONG
|
||
|
APIHOOK(RegQueryValueExA)(
|
||
|
HKEY hKey, // handle to key
|
||
|
LPCSTR lpValueName, // value name
|
||
|
LPDWORD lpReserved, // reserved
|
||
|
LPDWORD lpType, // type buffer
|
||
|
LPBYTE lpData, // data buffer
|
||
|
LPDWORD lpcbData // size of data buffer
|
||
|
)
|
||
|
{
|
||
|
DWORD cbOriginal = (lpcbData ? *lpcbData : 0); // save off the original buffer size
|
||
|
LPCSTR pszNewHKLMKey;
|
||
|
LONG lRet = ORIGINAL_API(RegQueryValueExA)(hKey,
|
||
|
lpValueName,
|
||
|
lpReserved,
|
||
|
lpType,
|
||
|
lpData,
|
||
|
lpcbData);
|
||
|
|
||
|
if ((lpValueName && lpcbData) && // (not simply checking for existance of the value...)
|
||
|
IsBadShellFolderKey(hKey, &pszNewHKLMKey))
|
||
|
{
|
||
|
CHAR szTemp[MAX_PATH];
|
||
|
|
||
|
if (lstrcmpiA(lpValueName, "Desktop") == 0) {
|
||
|
|
||
|
DPFN(
|
||
|
eDbgLevelInfo,
|
||
|
"[RegQueryValueExA] querying for 'Desktop' value\n");
|
||
|
|
||
|
if (lRet == ERROR_SUCCESS) {
|
||
|
lRet = GetAllUsersRegValueA((LPSTR)lpData,
|
||
|
cbOriginal,
|
||
|
lpcbData,
|
||
|
CSIDL_DESKTOP,
|
||
|
CSIDL_COMMON_DESKTOPDIRECTORY);
|
||
|
|
||
|
} else if (lRet == ERROR_MORE_DATA) {
|
||
|
|
||
|
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_COMMON_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, szTemp))) {
|
||
|
*lpcbData = MAX(*lpcbData, (DWORD)((lstrlenA(szTemp) + 1) * sizeof(CHAR)));
|
||
|
}
|
||
|
}
|
||
|
} else if (lstrcmpiA(lpValueName, "Start Menu") == 0) {
|
||
|
|
||
|
DPFN(
|
||
|
eDbgLevelInfo,
|
||
|
"[RegQueryValueExA] querying for 'Start Menu' value\n");
|
||
|
|
||
|
if (lRet == ERROR_SUCCESS) {
|
||
|
lRet = GetAllUsersRegValueA((LPSTR)lpData,
|
||
|
cbOriginal,
|
||
|
lpcbData,
|
||
|
CSIDL_STARTMENU,
|
||
|
CSIDL_COMMON_STARTMENU);
|
||
|
|
||
|
} else if (lRet == ERROR_MORE_DATA) {
|
||
|
|
||
|
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_COMMON_STARTMENU, NULL, SHGFP_TYPE_CURRENT, szTemp))) {
|
||
|
*lpcbData = MAX(*lpcbData, (DWORD)((lstrlenA(szTemp) + 1) * sizeof(CHAR)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else if (lstrcmpiA(lpValueName, "Startup") == 0) {
|
||
|
|
||
|
DPFN(
|
||
|
eDbgLevelInfo,
|
||
|
"[RegQueryValueExA] querying for 'Startup' value\n");
|
||
|
|
||
|
if (lRet == ERROR_SUCCESS) {
|
||
|
lRet = GetAllUsersRegValueA((LPSTR)lpData, cbOriginal, lpcbData, CSIDL_STARTUP, CSIDL_COMMON_STARTUP);
|
||
|
|
||
|
} else if (lRet == ERROR_MORE_DATA) {
|
||
|
|
||
|
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_COMMON_STARTUP, NULL, SHGFP_TYPE_CURRENT, szTemp))) {
|
||
|
*lpcbData = MAX(*lpcbData, (DWORD)((lstrlenA(szTemp) + 1) * sizeof(CHAR)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else if (lstrcmpiA(lpValueName, "Programs") == 0) {
|
||
|
|
||
|
DPFN(
|
||
|
eDbgLevelInfo,
|
||
|
"[RegQueryValueExA] querying for 'Programs' value\n");
|
||
|
|
||
|
if (lRet == ERROR_SUCCESS) {
|
||
|
lRet = GetAllUsersRegValueA((LPSTR)lpData, cbOriginal, lpcbData, CSIDL_PROGRAMS, CSIDL_COMMON_PROGRAMS);
|
||
|
|
||
|
} else if (lRet == ERROR_MORE_DATA) {
|
||
|
|
||
|
if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_COMMON_PROGRAMS, NULL, SHGFP_TYPE_CURRENT, szTemp))) {
|
||
|
*lpcbData = MAX(*lpcbData, (DWORD)((lstrlenA(szTemp) + 1) * sizeof(CHAR)));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return lRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL
|
||
|
NOTIFY_FUNCTION(
|
||
|
DWORD fdwReason
|
||
|
)
|
||
|
{
|
||
|
if (fdwReason == DLL_PROCESS_ATTACH) {
|
||
|
OSVERSIONINFOEX osvi = {0};
|
||
|
|
||
|
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||
|
|
||
|
if (GetVersionEx((OSVERSIONINFO*)&osvi)) {
|
||
|
|
||
|
if (!((VER_SUITE_TERMINAL & osvi.wSuiteMask) &&
|
||
|
!(VER_SUITE_SINGLEUSERTS & osvi.wSuiteMask))) {
|
||
|
//
|
||
|
// Only install hooks if we are not on a "Terminal Server"
|
||
|
// (aka "Application Server") machine.
|
||
|
//
|
||
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegQueryValueExA);
|
||
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyA);
|
||
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyExA);
|
||
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyA);
|
||
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyExA);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
HOOK_BEGIN
|
||
|
|
||
|
CALL_NOTIFY_FUNCTION
|
||
|
|
||
|
HOOK_END
|
||
|
|
||
|
|
||
|
IMPLEMENT_SHIM_END
|
||
|
|
||
|
|