windows-nt/Source/XPSP1/NT/windows/appcompat/shims/layer/profilesregqueryvalueex.cpp
2020-09-26 16:20:57 +08:00

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