4979 lines
113 KiB
C++
4979 lines
113 KiB
C++
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
VRegistry.cpp
|
|
|
|
Abstract:
|
|
|
|
A virtual registry for misbehaving registry readers.
|
|
|
|
This engine has 5 main features:
|
|
1. Key redirection
|
|
Eg: HKLM\Software -> HKLM\Hardware
|
|
2. Key and Value spoofing
|
|
Eg: HKLM\Software\VersionNumber can be made to appear as a valid
|
|
value
|
|
HKEY_DYN_DATA can appear valid
|
|
3. Expansion of REG_EXPAND_SZ value type to REG_SZ
|
|
Eg: %SystemRoot%\Media will result in C:\WINNT\Media
|
|
4. Support for EnumKey, EnumValue and QueryInfoKey on virtual keys
|
|
5. Support for CreateKey
|
|
|
|
Other features:
|
|
1. Strip leading '\' characters from keys
|
|
2. Add MAXIMUM_ALLOWED security attributes to all keys
|
|
3. Adjust parameters of QueryInfoKey to match Win95
|
|
4. Enable key deletion for key which still has subkeys
|
|
in order to match Win95 behavior for RegDeleteKey
|
|
5. Values and keys can be protected from modification and deletion
|
|
6. Custom triggers on opening a key.
|
|
7. Values that have extra data beyond end of string can be queried
|
|
even though the provided buffer is too small for extra data.
|
|
|
|
Known limitations:
|
|
No support for RegSetValue and RegSetValueEx other than known parameter
|
|
error and value protection.
|
|
|
|
Notes:
|
|
|
|
This is for apps with registry problems
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
01/10/2000 linstev Added support for RegEnumKey, RegEnumValue
|
|
01/10/2000 linstev Added support for RegCreateKey
|
|
05/05/2000 linstev Parameterized
|
|
10/03/2000 maonis Bug fixes and got rid of the cleanup code in process detach.
|
|
10/30/2000 andyseti Added support for RegDeleteKey
|
|
02/27/2001 robkenny Converted to use CString
|
|
08/07/2001 mikrause Added protectors, enumeration of virtual & non-virtual keys & values,
|
|
triggers on opening a key, and querying values with extra data
|
|
and a too small buffer.
|
|
10/12/2001 mikrause Added support for custom callbacks on SetValue.
|
|
Reimplemented value protectors as do-nothing callbacks.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
IMPLEMENT_SHIM_BEGIN(VirtualRegistry)
|
|
#include "ShimHookMacro.h"
|
|
#include "VRegistry.h"
|
|
#include "VRegistry_Worker.h"
|
|
|
|
|
|
// Allows us to have only one code path for dumping all APIs or just the APIs
|
|
// that had errors
|
|
#define ELEVEL(lRet) SUCCESS(lRet) ? eDbgLevelInfo : eDbgLevelError
|
|
|
|
CRITICAL_SECTION csRegCriticalSection;
|
|
|
|
// Global instance of Virtual Registry class
|
|
CVirtualRegistry VRegistry;
|
|
|
|
// Used to enable win9x only features
|
|
BOOL g_bWin9x = TRUE;
|
|
|
|
/*++
|
|
|
|
Class Description:
|
|
|
|
This class is designed to simplify locking logic. If an object of this
|
|
class is instantiated, then internal lock will be taken. As soon as object
|
|
is destroyed lock will be released. We also check if the registry has been
|
|
initialized. This has to happen late because we don't get notified after
|
|
we've been loaded.
|
|
|
|
History:
|
|
|
|
01/10/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
static BOOL g_bInitialized = FALSE;
|
|
|
|
BOOL ParseCommandLineA(LPCSTR lpCommandLine);
|
|
|
|
class CRegLock
|
|
{
|
|
public:
|
|
CRegLock()
|
|
{
|
|
EnterCriticalSection(&csRegCriticalSection);
|
|
if (!g_bInitialized)
|
|
{
|
|
|
|
VENTRY* ventry = g_pVList;
|
|
while (ventry->pfnBuilder)
|
|
{
|
|
if (ventry->bShouldCall)
|
|
{
|
|
DPFN( eDbgLevelInfo, " %S", ventry->cName);
|
|
ventry->pfnBuilder(ventry->szParam);
|
|
if (ventry->szParam)
|
|
{
|
|
free(ventry->szParam);
|
|
}
|
|
ventry->bShouldCall = FALSE;
|
|
}
|
|
ventry++;
|
|
}
|
|
g_bInitialized = TRUE;
|
|
}
|
|
}
|
|
~CRegLock()
|
|
{
|
|
LeaveCriticalSection(&csRegCriticalSection);
|
|
}
|
|
};
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Remove leading slash from an Unicode string
|
|
|
|
Arguments:
|
|
|
|
IN lpSubKey - path to string
|
|
|
|
Return Value:
|
|
|
|
Subkey without leading \
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LPCWSTR
|
|
TrimSlashW(
|
|
IN OUT LPCWSTR lpSubKey
|
|
)
|
|
{
|
|
if (!lpSubKey)
|
|
{
|
|
return lpSubKey;
|
|
}
|
|
|
|
LPWSTR lpNew = (LPWSTR) lpSubKey;
|
|
|
|
#define REG_MACHINE L"\\Registry\\Machine"
|
|
#define REG_USER L"\\Registry\\User"
|
|
|
|
//
|
|
// Pull off the old NT4 legacy stuff. This only works on NT4, but we're
|
|
// making it for everyone since it's low risk.
|
|
//
|
|
if (wcsistr(lpNew, REG_MACHINE) == lpNew)
|
|
{
|
|
LOGN( eDbgLevelError, "[TrimSlashW] Bypass \\Registry\\Machine");
|
|
lpNew += wcslen(REG_MACHINE);
|
|
}
|
|
else if (wcsistr(lpNew, REG_USER) == lpNew)
|
|
{
|
|
LOGN( eDbgLevelError, "[TrimSlashW] Bypass \\Registry\\User");
|
|
lpNew += wcslen(REG_USER);
|
|
}
|
|
|
|
if (*lpNew == L'\\')
|
|
{
|
|
LOGN( eDbgLevelError, "[TrimSlashW] Removed slash from key beginning");
|
|
lpNew++;
|
|
}
|
|
|
|
return lpNew;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Convert a key from registry format to virtual registry format. i.e.:
|
|
HKEY, Path -> VPath. The VPath format has the base included as "HKLM"
|
|
instead of HKEY_LOCAL_MACHINE etc.
|
|
|
|
Algorithm:
|
|
1. Case the different keys and output a 4 letter string
|
|
2. Append subkey if available
|
|
|
|
Arguments:
|
|
|
|
IN hkBase - Base key, eg: HKEY_LOCAL_MACHINE
|
|
IN lpSubKey - Subkey, eg: SOFTWARE
|
|
OUT lpPath - Output, eg: HKLM\SOFTWARE
|
|
|
|
Return Value:
|
|
|
|
A string path of the form HKLM\SOFTWARE
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LPWSTR
|
|
MakePath(
|
|
IN HKEY hkBase,
|
|
IN LPCWSTR lpKey,
|
|
IN LPCWSTR lpSubKey
|
|
)
|
|
{
|
|
DWORD dwSize = 0;
|
|
|
|
if (hkBase)
|
|
{
|
|
// Length of HKCU + NULL
|
|
dwSize = 5;
|
|
}
|
|
if (lpKey)
|
|
{
|
|
dwSize += wcslen(lpKey) + 1;
|
|
}
|
|
if (lpSubKey)
|
|
{
|
|
dwSize += wcslen(lpSubKey) + 1;
|
|
}
|
|
|
|
LPWSTR lpPath = (LPWSTR) malloc((dwSize + 1) * sizeof(WCHAR));
|
|
|
|
if (!lpPath)
|
|
{
|
|
if (dwSize)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
*lpPath = L'\0';
|
|
|
|
HRESULT hr;
|
|
if (hkBase)
|
|
{
|
|
if (hkBase == HKEY_CLASSES_ROOT)
|
|
{
|
|
hr = StringCchCopyW(lpPath, dwSize + 1, L"HKCR");
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else if (hkBase == HKEY_CURRENT_CONFIG)
|
|
{
|
|
hr = StringCchCopyW(lpPath, dwSize + 1, L"HKCC");
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else if (hkBase == HKEY_CURRENT_USER)
|
|
{
|
|
hr = StringCchCopyW(lpPath, dwSize + 1, L"HKCU");
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else if (hkBase == HKEY_LOCAL_MACHINE)
|
|
{
|
|
hr = StringCchCopyW(lpPath, dwSize + 1, L"HKLM");
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else if (hkBase == HKEY_USERS)
|
|
{
|
|
hr = StringCchCopyW(lpPath, dwSize + 1, L"HKUS");
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else if (hkBase == HKEY_PERFORMANCE_DATA)
|
|
{
|
|
hr = StringCchCopyW(lpPath, dwSize + 1, L"HKPD");
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else if (hkBase == HKEY_DYN_DATA)
|
|
{
|
|
hr = StringCchCopyW(lpPath, dwSize + 1, L"HKDD");
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelWarning,
|
|
"Key not found: %08lx - did not get an openkey or createkey",
|
|
hkBase);
|
|
}
|
|
}
|
|
|
|
// Add the key
|
|
if (lpKey)
|
|
{
|
|
if (wcslen(lpPath) != 0)
|
|
{
|
|
hr = StringCchCatW(lpPath, dwSize + 1, L"\\");
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
hr = StringCchCatW(lpPath, dwSize + 1, lpKey);
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
|
|
// Add the subkey
|
|
if (lpSubKey)
|
|
{
|
|
if (wcslen(lpPath) != 0)
|
|
{
|
|
hr = StringCchCatW(lpPath, dwSize + 1, L"\\");
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
hr = StringCchCatW(lpPath, dwSize + 1, lpSubKey);
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
|
|
// The key name can have a trailing slash, so we clean this up
|
|
DWORD dwLen = wcslen(lpPath);
|
|
if (dwLen && (lpPath[dwLen - 1] == L'\\'))
|
|
{
|
|
lpPath[dwLen - 1] = L'\0';
|
|
}
|
|
|
|
return lpPath;
|
|
|
|
ErrorCleanup:
|
|
free(lpPath);
|
|
return NULL;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Convert a key from Path format into key and subkey format.
|
|
|
|
Algorithm:
|
|
1. Case the different keys and output a 4 letter string
|
|
2. Append subkey if available
|
|
|
|
Arguments:
|
|
|
|
IN lpPath - Path, eg: HKLM\Software
|
|
OUT hkBase - Key, eg: HKEY_LOCAL_MACHINE
|
|
OUT lpSubKey - Subkey, eg: Software
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LPWSTR
|
|
SplitPath(
|
|
IN LPCWSTR lpPath,
|
|
OUT HKEY *hkBase
|
|
)
|
|
{
|
|
LPWSTR p = (LPWSTR) lpPath;
|
|
|
|
// Find first \ or NULL
|
|
while (*p && (*p != L'\\')) p++;
|
|
|
|
if (wcsncmp(lpPath, L"HKCR", 4) == 0)
|
|
*hkBase = HKEY_CLASSES_ROOT;
|
|
|
|
else if (wcsncmp(lpPath, L"HKCC", 4) == 0)
|
|
*hkBase = HKEY_CURRENT_CONFIG;
|
|
|
|
else if (wcsncmp(lpPath, L"HKCU", 4) == 0)
|
|
*hkBase = HKEY_CURRENT_USER;
|
|
|
|
else if (wcsncmp(lpPath, L"HKLM", 4) == 0)
|
|
*hkBase = HKEY_LOCAL_MACHINE;
|
|
|
|
else if (wcsncmp(lpPath, L"HKUS", 4) == 0)
|
|
*hkBase = HKEY_USERS;
|
|
|
|
else if (wcsncmp(lpPath, L"HKPD", 4) == 0)
|
|
*hkBase = HKEY_PERFORMANCE_DATA;
|
|
|
|
else if (wcsncmp(lpPath, L"HKDD", 4) == 0)
|
|
*hkBase = HKEY_DYN_DATA;
|
|
|
|
else
|
|
*hkBase = 0;
|
|
|
|
// Don't allow an invalid base key to get through.
|
|
if (*hkBase && lpPath[4] != '\\')
|
|
{
|
|
*hkBase = 0;
|
|
}
|
|
|
|
if (*p)
|
|
{
|
|
p++;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add a virtual key: a key contains other keys and values and will behave
|
|
like a normal registry key, but of course has no persistent storage.
|
|
|
|
Algorithm:
|
|
1. The input string is split apart and a tree is created recursively
|
|
2. The key is created only if it doesn't already exist
|
|
|
|
Arguments:
|
|
|
|
IN lpPath - Path to key, eg: "HKLM\\Software"
|
|
|
|
Return Value:
|
|
|
|
Pointer to key or NULL
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VIRTUALKEY *
|
|
VIRTUALKEY::AddKey(
|
|
IN LPCWSTR lpPath
|
|
)
|
|
{
|
|
VIRTUALKEY *key;
|
|
LPWSTR p = (LPWSTR)lpPath;
|
|
|
|
// Find first \ or NULL
|
|
while (*p && (*p != L'\\')) p++;
|
|
|
|
// Check if this part already exists
|
|
key = keys;
|
|
while (key != NULL)
|
|
{
|
|
if (_wcsnicmp(lpPath, key->wName, p - lpPath) == 0)
|
|
{
|
|
if (*p == L'\\')
|
|
{
|
|
// Continue the search
|
|
return key->AddKey(p + 1);
|
|
}
|
|
else
|
|
{
|
|
// We already added this key
|
|
return key;
|
|
}
|
|
}
|
|
key = key->next;
|
|
}
|
|
|
|
// Create a new key
|
|
|
|
key = (VIRTUALKEY *) malloc(sizeof(VIRTUALKEY));
|
|
if (!key)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(key, sizeof(VIRTUALKEY));
|
|
|
|
//
|
|
// Still use wcsncpy, because here it specifies number of characters
|
|
// to copy, not size of the destination buffer. Add in check
|
|
// for destination buffer size.
|
|
//
|
|
if ( (p - lpPath) > sizeof(key->wName)/sizeof(WCHAR))
|
|
{
|
|
free (key);
|
|
return NULL;
|
|
}
|
|
wcsncpy((LPWSTR)key->wName, lpPath, p - lpPath);
|
|
key->next = keys;
|
|
keys = key;
|
|
|
|
DPFN( eDbgLevelSpew, "Adding Key %S", key->wName);
|
|
|
|
if (*p == L'\0')
|
|
{
|
|
// We are at the end of the chain, so just return this one
|
|
return key;
|
|
}
|
|
else
|
|
{
|
|
// More subkeys to go
|
|
return key->AddKey(p + 1);
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add a value to a virtual key. The actual registry key may exist and the
|
|
value may even exist, but this value will override.
|
|
|
|
Algorithm:
|
|
1. If lpData is a string and cbData is 0, calculate the size
|
|
2. Add this value (no duplicate checking)
|
|
|
|
Arguments:
|
|
|
|
IN lpValueName - Value name
|
|
IN dwType - Type of key; eg: REG_SZ, REG_DWORD etc
|
|
IN lpData - Data, use unicode if string
|
|
IN cbData - Size of lpData
|
|
|
|
Return Value:
|
|
|
|
Pointer to value or NULL
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VIRTUALVAL *
|
|
VIRTUALKEY::AddValue(
|
|
IN LPCWSTR lpValueName,
|
|
IN DWORD dwType,
|
|
IN BYTE *lpData,
|
|
IN DWORD cbData
|
|
)
|
|
{
|
|
// Parameter validation
|
|
if (lpData == NULL && cbData != 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
VIRTUALVAL *value = (VIRTUALVAL *) malloc(sizeof(VIRTUALVAL));
|
|
if (!value)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(value, sizeof(VIRTUALVAL));
|
|
|
|
// Auto calculate size if cbData is 0
|
|
if (lpData && (cbData == 0))
|
|
{
|
|
switch (dwType)
|
|
{
|
|
case REG_SZ:
|
|
case REG_EXPAND_SZ:
|
|
cbData = wcslen((LPWSTR)lpData)*2 + sizeof(WCHAR);
|
|
break;
|
|
|
|
case REG_DWORD:
|
|
cbData = sizeof(DWORD);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// lpValueName can == NULL, which means default value
|
|
if (lpValueName)
|
|
{
|
|
HRESULT hr = StringCchCopy(value->wName, sizeof(value->wName)/sizeof(WCHAR), lpValueName);
|
|
if (FAILED(hr))
|
|
{
|
|
free(value);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
if (cbData)
|
|
{
|
|
// Make a copy of the data if needed
|
|
value->lpData = (BYTE *) malloc(cbData);
|
|
|
|
if (!value->lpData)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
free(value);
|
|
return NULL;
|
|
}
|
|
|
|
MoveMemory(value->lpData, lpData, cbData);
|
|
value->cbData = cbData;
|
|
}
|
|
|
|
value->pfnQueryValue = NULL;
|
|
value->pfnSetValue = NULL;
|
|
value->dwType = dwType;
|
|
value->next = values;
|
|
values = value;
|
|
|
|
if (lpData && ((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)))
|
|
{
|
|
DPFN( eDbgLevelSpew, "Adding Value %S\\%S = %S", wName, lpValueName, lpData);
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelSpew, "Adding Value %S\\%S", wName, lpValueName);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add a dword value to a key. Calls off to AddValue.
|
|
|
|
Arguments:
|
|
|
|
IN lpValueName - Value name
|
|
IN Value - DWord value
|
|
|
|
Return Value:
|
|
|
|
Pointer to a virtual dword value
|
|
|
|
History:
|
|
|
|
05/25/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VIRTUALVAL *
|
|
VIRTUALKEY::AddValueDWORD(
|
|
IN LPCWSTR lpValueName,
|
|
IN DWORD dwValue
|
|
)
|
|
{
|
|
return AddValue(lpValueName, REG_DWORD, (LPBYTE)&dwValue);
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add an expander to a key. An expander causes QueryValue to expand the
|
|
REG_EXPAND_SZ type to a REG_SZ type. The expander itself is just
|
|
a virtual value which allows us to intercept queries to it.
|
|
|
|
Arguments:
|
|
|
|
IN lpValueName - Value name
|
|
|
|
Return Value:
|
|
|
|
Pointer to a virtual value
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VIRTUALVAL *
|
|
VIRTUALKEY::AddExpander(
|
|
IN LPCWSTR lpValueName
|
|
)
|
|
{
|
|
VIRTUALVAL *value = AddValue(lpValueName, REG_SZ, 0, 0);
|
|
|
|
if (value)
|
|
{
|
|
value->pfnQueryValue = VR_Expand;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add a protector on a value. A protector causes SetValue to
|
|
be ignored. This is implemented through a custom setvalue
|
|
callback that does nothing.
|
|
|
|
Arguments:
|
|
|
|
IN lpValueName - Value name
|
|
|
|
Return Value:
|
|
|
|
Pointer to a virtual value
|
|
|
|
History:
|
|
|
|
10/12/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
VIRTUALVAL *
|
|
VIRTUALKEY::AddProtector(
|
|
IN LPCWSTR lpValueName
|
|
)
|
|
{
|
|
VIRTUALVAL *value = AddValue(lpValueName, REG_SZ, 0, 0);
|
|
|
|
if (value)
|
|
{
|
|
value->pfnSetValue = VR_Protect;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add a custom queryvalue routine
|
|
|
|
Arguments:
|
|
|
|
IN lpValueName - Value name
|
|
IN pfnQueryValue - routine to call when this value is queried
|
|
|
|
Return Value:
|
|
|
|
Pointer to a virtual value
|
|
|
|
History:
|
|
|
|
07/18/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VIRTUALVAL *
|
|
VIRTUALKEY::AddCustom(
|
|
IN LPCWSTR lpValueName,
|
|
_pfn_QueryValue pfnQueryValue
|
|
)
|
|
{
|
|
VIRTUALVAL *value = AddValue(lpValueName, REG_SZ, 0, 0);
|
|
|
|
if (value)
|
|
{
|
|
value->pfnQueryValue = pfnQueryValue;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add a custom setvalue routine
|
|
|
|
Arguments:
|
|
|
|
IN lpValueName - Value name
|
|
IN pfnSetValue - routine to call when this value is set
|
|
|
|
Return Value:
|
|
|
|
Pointer to a virtual value
|
|
|
|
History:
|
|
|
|
11/06/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
VIRTUALVAL *
|
|
VIRTUALKEY::AddCustomSet(
|
|
IN LPCWSTR lpValueName,
|
|
_pfn_SetValue pfnSetValue
|
|
)
|
|
{
|
|
VIRTUALVAL *value = AddValue(lpValueName, REG_SZ, 0, 0);
|
|
|
|
if (value)
|
|
{
|
|
value->pfnSetValue = pfnSetValue;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Find a subkey of a key.
|
|
|
|
Algorithm:
|
|
1. Recursively search the tree for the matching subkey
|
|
|
|
Arguments:
|
|
|
|
IN lpKeyName - Name of key to find
|
|
|
|
Return Value:
|
|
|
|
Pointer to value or NULL
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VIRTUALKEY *
|
|
VIRTUALKEY::FindKey(
|
|
IN LPCWSTR lpPath
|
|
)
|
|
{
|
|
VIRTUALKEY *key = keys;
|
|
LPWSTR p = (LPWSTR)lpPath;
|
|
|
|
if (!lpPath)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Find first \ or NULL
|
|
while (*p && (*p != L'\\')) p++;
|
|
|
|
// recursively look for the key
|
|
while (key)
|
|
{
|
|
if (_wcsnicmp(
|
|
lpPath,
|
|
key->wName,
|
|
max((DWORD_PTR)(p - lpPath), wcslen(key->wName))) == 0)
|
|
{
|
|
if (*p == L'\\')
|
|
{
|
|
key = key->FindKey(p + 1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
key = key->next;
|
|
}
|
|
|
|
// We're at the end of the chain
|
|
return key;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Find a value in a key.
|
|
|
|
Arguments:
|
|
|
|
IN key - Key used for expanders; unused at this time
|
|
IN lpValueName - Value name
|
|
|
|
Return Value:
|
|
|
|
Pointer to value or NULL
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VIRTUALVAL *
|
|
VIRTUALKEY::FindValue(
|
|
IN LPCWSTR lpValueName
|
|
)
|
|
{
|
|
VIRTUALVAL *value = values;
|
|
WCHAR wDef[1] = L"";
|
|
LPWSTR lpName;
|
|
|
|
if (!lpValueName)
|
|
{
|
|
lpName = (LPWSTR)wDef;
|
|
}
|
|
else
|
|
{
|
|
lpName = (LPWSTR)lpValueName;
|
|
}
|
|
|
|
// Find the value
|
|
while (value)
|
|
{
|
|
if (_wcsicmp(lpName, value->wName) == 0)
|
|
{
|
|
LOGN( eDbgLevelWarning, "[FindValue] Using virtual value: %S", value->wName);
|
|
break;
|
|
}
|
|
value = value->next;
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Free the subkeys and values belonging to a key
|
|
|
|
Algorithm:
|
|
1. Free all values belonging to a key, including any data
|
|
2. Free all subkeys recursively
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
VIRTUALKEY::Free()
|
|
{
|
|
VIRTUALVAL *value = values;
|
|
VIRTUALKEY *key = keys;
|
|
|
|
while (value)
|
|
{
|
|
values = value->next;
|
|
if (value->lpData)
|
|
{
|
|
free((PVOID) value->lpData);
|
|
}
|
|
free((PVOID) value);
|
|
value = values;
|
|
}
|
|
|
|
while (key)
|
|
{
|
|
keys = key->next;
|
|
key->Free();
|
|
free((PVOID) key);
|
|
key = keys;
|
|
}
|
|
|
|
DPFN( eDbgLevelSpew, "Free keys and values from %S", wName);
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Allocate a new enum entry
|
|
|
|
Arguments:
|
|
|
|
IN wzPath - Key path or value name of entry.
|
|
IN next - Next entry in the list.
|
|
|
|
Return Value:
|
|
|
|
Pointer to new entry or NULL
|
|
|
|
History:
|
|
|
|
08/21/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
ENUMENTRY*
|
|
CreateNewEnumEntry(
|
|
IN LPWSTR wzPath,
|
|
IN ENUMENTRY* next)
|
|
{
|
|
ENUMENTRY* enumEntry;
|
|
enumEntry = (ENUMENTRY*)malloc(sizeof(ENUMENTRY));
|
|
if (enumEntry == NULL)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(enumEntry, sizeof(ENUMENTRY));
|
|
|
|
enumEntry->wzName = (LPWSTR)malloc((wcslen(wzPath) + 1)*sizeof(WCHAR));
|
|
if (enumEntry->wzName == NULL)
|
|
{
|
|
free(enumEntry);
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return NULL;
|
|
}
|
|
|
|
HRESULT hr = StringCchCopyW(enumEntry->wzName, wcslen(wzPath)+1, wzPath);
|
|
if (FAILED(hr))
|
|
{
|
|
free(enumEntry->wzName);
|
|
free(enumEntry);
|
|
return NULL;
|
|
}
|
|
|
|
enumEntry->next = next;
|
|
|
|
return enumEntry;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add enumeration entries to a list. Templatized,
|
|
so the same code works for keys or values.
|
|
|
|
Arguments:
|
|
|
|
IN entryHead - Head of the list containing virtual keys or values.
|
|
IN enumFunc - Enumeration function to use. Either RegEnumKey or RegEnumValue
|
|
|
|
Return Value:
|
|
|
|
Pointer to the head of the entry list, or NULL.
|
|
|
|
History:
|
|
|
|
08/21/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
template<class T>
|
|
ENUMENTRY*
|
|
OPENKEY::AddEnumEntries(T* entryHead, _pfn_EnumFunction enumFunc)
|
|
{
|
|
LONG lRet;
|
|
DWORD dwIndex;
|
|
DWORD dwSize;
|
|
WCHAR wzName[MAX_PATH + 1];
|
|
ENUMENTRY* enumEntryList = NULL;
|
|
ENUMENTRY* newEnumEntry = NULL;
|
|
|
|
// Add virtual entries to the list.
|
|
T* entry = entryHead;
|
|
while (entry)
|
|
{
|
|
// Create a new entry.
|
|
newEnumEntry = CreateNewEnumEntry(entry->wName, enumEntryList);
|
|
|
|
if (newEnumEntry != NULL)
|
|
{
|
|
enumEntryList = newEnumEntry;
|
|
}
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
// Now non-virtuals.
|
|
if (bVirtual == FALSE)
|
|
{
|
|
dwIndex = 0;
|
|
|
|
for (;;)
|
|
{
|
|
dwSize = MAX_PATH * sizeof(WCHAR);
|
|
lRet = enumFunc(hkOpen, dwIndex, wzName, &dwSize, NULL, NULL, NULL, NULL);
|
|
|
|
// No more items, we're done.
|
|
if (lRet == ERROR_NO_MORE_ITEMS)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check for error.
|
|
// On Win2K, this can return more data if there are additional keys.
|
|
//
|
|
if (lRet != ERROR_SUCCESS && lRet != ERROR_MORE_DATA)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// Check if this key is a duplicate.
|
|
entry = entryHead;
|
|
while (entry)
|
|
{
|
|
if (_wcsicmp(entry->wName, wzName) == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
entry = entry->next;
|
|
}
|
|
|
|
// Add this key to the list, if it's not a duplicate.
|
|
if (entry == NULL)
|
|
{
|
|
// Create a new entry.
|
|
newEnumEntry = CreateNewEnumEntry(wzName, enumEntryList);
|
|
if (newEnumEntry != NULL)
|
|
{
|
|
enumEntryList = newEnumEntry;
|
|
}
|
|
}
|
|
dwIndex++;
|
|
}
|
|
}
|
|
|
|
return enumEntryList;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Builds the list of enumerated keys and values.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
08/10/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
OPENKEY::BuildEnumList()
|
|
{
|
|
VIRTUALKEY* keyHead = NULL;
|
|
VIRTUALVAL* valHead = NULL;
|
|
|
|
if (vkey)
|
|
{
|
|
keyHead = vkey->keys;
|
|
valHead = vkey->values;
|
|
}
|
|
|
|
enumKeys = AddEnumEntries(keyHead, (_pfn_EnumFunction)ORIGINAL_API(RegEnumKeyExW));
|
|
enumValues = AddEnumEntries(valHead, (_pfn_EnumFunction)ORIGINAL_API(RegEnumValueW));
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Flushes all enumerated data..
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
08/10/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
OPENKEY::FlushEnumList()
|
|
{
|
|
ENUMENTRY *enumentry;
|
|
|
|
DPFN(eDbgLevelInfo, "Flushing enumeration data for %S", wzPath);
|
|
while (enumKeys)
|
|
{
|
|
enumentry = enumKeys;
|
|
enumKeys = enumKeys->next;
|
|
|
|
if (enumentry->wzName)
|
|
{
|
|
free(enumentry->wzName);
|
|
}
|
|
|
|
free(enumentry);
|
|
}
|
|
|
|
while (enumValues)
|
|
{
|
|
enumentry = enumValues;
|
|
enumValues = enumValues->next;
|
|
|
|
if (enumentry->wzName)
|
|
{
|
|
free(enumentry->wzName);
|
|
}
|
|
|
|
free(enumentry);
|
|
}
|
|
|
|
enumKeys = enumValues = NULL;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Initialize the virtual registry. This would ordinarily go into the
|
|
constructor, but because of the shim architecture, we need to explicity
|
|
initialize and free the virtual registry.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
CVirtualRegistry::Init()
|
|
{
|
|
OpenKeys = NULL;
|
|
Redirectors = NULL;
|
|
KeyProtectors = NULL;
|
|
OpenKeyTriggers = NULL;
|
|
|
|
Root = (VIRTUALKEY *) malloc(sizeof(VIRTUALKEY));
|
|
if (!Root)
|
|
{
|
|
DPFN(eDbgLevelError, szOutOfMemory);
|
|
return FALSE;
|
|
}
|
|
ZeroMemory(Root, sizeof(VIRTUALKEY));
|
|
HRESULT hr = StringCchCopyW(Root->wName, sizeof(Root->wName)/sizeof(WCHAR), L"ROOT");
|
|
if (FAILED(hr))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
DPFN( eDbgLevelSpew, "Initializing Virtual Registry");
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Free the lists contained by the virtual registry. This includes keys,
|
|
their values and redirectors.
|
|
|
|
Algorithm:
|
|
1. Free virtual root key which recursively frees subkeys and values
|
|
2. Free open keys
|
|
3. Free redirectors
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
CVirtualRegistry::Free()
|
|
{
|
|
OPENKEY *key;
|
|
REDIRECTOR *redirect;
|
|
OPENKEYTRIGGER *trigger;
|
|
PROTECTOR *protector;
|
|
|
|
DPFN( eDbgLevelSpew, "Freeing Virtual Registry");
|
|
|
|
// Free Root and all subkeys/values
|
|
if (Root)
|
|
{
|
|
Root->Free();
|
|
free(Root);
|
|
Root = NULL;
|
|
}
|
|
|
|
// Delete all enumeration data.
|
|
FlushEnumLists();
|
|
|
|
// Free list of open registry keys
|
|
key = OpenKeys;
|
|
while (key)
|
|
{
|
|
OpenKeys = key->next;
|
|
free(key->wzPath);
|
|
free(key);
|
|
key = OpenKeys;
|
|
}
|
|
|
|
// Free redirectors
|
|
redirect = Redirectors;
|
|
while (redirect)
|
|
{
|
|
Redirectors = redirect->next;
|
|
free(redirect->wzPath);
|
|
free(redirect->wzPathNew);
|
|
free(redirect);
|
|
redirect = Redirectors;
|
|
}
|
|
|
|
// Free open key triggers
|
|
trigger = OpenKeyTriggers;
|
|
while(trigger)
|
|
{
|
|
OpenKeyTriggers = trigger->next;
|
|
free(trigger->wzPath);
|
|
free(trigger);
|
|
trigger = OpenKeyTriggers;
|
|
}
|
|
|
|
// Free Protectors
|
|
protector = KeyProtectors;
|
|
while(protector)
|
|
{
|
|
KeyProtectors = protector->next;
|
|
free(protector->wzPath);
|
|
free(protector);
|
|
protector = KeyProtectors;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Create a dummy key for use as a virtual key. We need to have unique handles
|
|
in order to look up the keys, so by creating a key off HKLM, we can be
|
|
sure it won't fail.
|
|
|
|
We can't damage the registry like this because writes to this key will fail.
|
|
Calls to QueryValue, QueryInfo and EnumKey will work correctly because the
|
|
virtual registry is used in preference to the real one.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Dummy key
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
HKEY
|
|
CVirtualRegistry::CreateDummyKey()
|
|
{
|
|
HKEY key = NULL;
|
|
|
|
LONG lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software", 0, KEY_READ, &key);
|
|
if (lRet != ERROR_SUCCESS)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Find an open key in the list of open keys.
|
|
|
|
Algorithm:
|
|
1. Search the list of open keys for a match
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Open HKEY
|
|
|
|
Return Value:
|
|
|
|
Pointer to a key or NULL
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
OPENKEY *
|
|
CVirtualRegistry::FindOpenKey(
|
|
IN HKEY hKey
|
|
)
|
|
{
|
|
OPENKEY *key = OpenKeys;
|
|
|
|
while (key)
|
|
{
|
|
if (key->hkOpen == hKey)
|
|
{
|
|
return key;
|
|
}
|
|
key = key->next;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
If this key is to be redirected, we adjust the path to the redirected
|
|
version. This works even if the requested path is a 'subpath' of a
|
|
redirector, eg:
|
|
Input = HKLM\Software\Test
|
|
Redirector = HKLM\Software -> HKLM\Hardware
|
|
Output = HKLM\Hardware\Test
|
|
If no redirector is present for this key/path, then lpPath is unchanged
|
|
|
|
Algorithm:
|
|
1. Find a key whose base is a redirector
|
|
2. Substitute the new base for the key
|
|
|
|
Arguments:
|
|
|
|
IN OUT lpPath - Path to redirect
|
|
|
|
Return Value:
|
|
|
|
TRUE if redirected
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
CVirtualRegistry::CheckRedirect(
|
|
IN OUT LPWSTR *lpPath
|
|
)
|
|
{
|
|
REDIRECTOR *redirect = Redirectors;
|
|
DWORD sza = wcslen(*lpPath);
|
|
|
|
// Go through the list of redirectors
|
|
while (redirect)
|
|
{
|
|
DWORD szb = wcslen(redirect->wzPath);
|
|
|
|
if ((szb <= sza) &&
|
|
(_wcsnicmp(*lpPath, redirect->wzPath, szb) == 0) &&
|
|
((*lpPath)[szb] == L'\\' || (*lpPath)[szb] == L'\0'))
|
|
{
|
|
WCHAR *p = *lpPath + szb;
|
|
|
|
DWORD cchPathSize = wcslen(redirect->wzPathNew) + wcslen(p) + 1;
|
|
LPWSTR wzPathNew = (LPWSTR) malloc(cchPathSize * sizeof(WCHAR));
|
|
if (wzPathNew)
|
|
{
|
|
HRESULT hr;
|
|
hr = StringCchCopyW(wzPathNew, cchPathSize, redirect->wzPathNew);
|
|
if (FAILED(hr))
|
|
{
|
|
free (wzPathNew);
|
|
return FALSE;
|
|
}
|
|
hr = StringCchCatW(wzPathNew, cchPathSize, p);
|
|
if (FAILED(hr))
|
|
{
|
|
free(wzPathNew);
|
|
return FALSE;
|
|
}
|
|
|
|
// return the new path
|
|
LOGN( eDbgLevelWarning, "Redirecting: %S -> %S", *lpPath, wzPathNew);
|
|
|
|
free(*lpPath);
|
|
*lpPath = wzPathNew;
|
|
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return FALSE;
|
|
}
|
|
}
|
|
redirect = redirect->next;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
|
|
Returns true if a protector guards this key.
|
|
This will even work on a subkey of a protector.
|
|
|
|
Arguments:
|
|
|
|
IN lpPath - Path to protect
|
|
|
|
Return Value:
|
|
|
|
TRUE if protected
|
|
|
|
History:
|
|
|
|
08/07/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
CVirtualRegistry::CheckProtected(
|
|
IN LPWSTR lpPath
|
|
)
|
|
{
|
|
PROTECTOR *protect;
|
|
|
|
DWORD sza = wcslen(lpPath);
|
|
DWORD szb;
|
|
|
|
protect = KeyProtectors;
|
|
while (protect)
|
|
{
|
|
szb = wcslen(protect->wzPath);
|
|
|
|
// Check if we have a key or subkey match.
|
|
if ((szb <= sza) &&
|
|
(_wcsnicmp(protect->wzPath, lpPath, szb) == 0) &&
|
|
(lpPath[szb] == L'\\' || lpPath[szb] == L'\0'))
|
|
{
|
|
// Protector found.
|
|
LOGN( eDbgLevelWarning, "\tProtecting: %S", lpPath);
|
|
return TRUE;
|
|
}
|
|
|
|
protect = protect->next;
|
|
}
|
|
|
|
// Fell through, no protector found.
|
|
return FALSE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Checks if any triggers should be called on this path,
|
|
and calls them.
|
|
|
|
Arguments:
|
|
|
|
IN lpPath - Path to check triggers for.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
08/09/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
CVirtualRegistry::CheckTriggers(
|
|
IN LPWSTR lpPath)
|
|
{
|
|
OPENKEYTRIGGER *trigger;
|
|
DWORD sza, szb;
|
|
|
|
sza = wcslen(lpPath);
|
|
trigger = OpenKeyTriggers;
|
|
|
|
//
|
|
// Loop through all triggers and check. Even after finding a match,
|
|
// keep repeating, because a single OpenKey can cause multiple triggers.
|
|
//
|
|
while (trigger)
|
|
{
|
|
szb = wcslen(trigger->wzPath);
|
|
if ((szb <= sza) &&
|
|
(_wcsnicmp(lpPath, trigger->wzPath, szb)==0) &&
|
|
(lpPath[szb] == L'\\' || lpPath[szb] == L'\0'))
|
|
{
|
|
DPFN(eDbgLevelInfo, "Triggering %S on opening of %S", trigger->wzPath, lpPath);
|
|
trigger->pfnTrigger(lpPath);
|
|
}
|
|
|
|
trigger = trigger->next;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Flushes all enumerated lists.
|
|
|
|
Arguments:
|
|
|
|
IN lpPath - Path to redirect, eg: HKLM\Software\Microsoft
|
|
IN lpPathNew - Redirect to this path
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VOID
|
|
CVirtualRegistry::FlushEnumLists()
|
|
{
|
|
OPENKEY *key;
|
|
|
|
key = OpenKeys;
|
|
while (key)
|
|
{
|
|
key->FlushEnumList();
|
|
key = key->next;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add a redirector to the virtual registry. See CheckRedirect().
|
|
|
|
Arguments:
|
|
|
|
IN lpPath - Path to redirect, eg: HKLM\Software\Microsoft
|
|
IN lpPathNew - Redirect to this path
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
REDIRECTOR *
|
|
CVirtualRegistry::AddRedirect(
|
|
IN LPCWSTR lpPath,
|
|
IN LPCWSTR lpPathNew)
|
|
{
|
|
REDIRECTOR *redirect = (REDIRECTOR *) malloc(sizeof(REDIRECTOR));
|
|
|
|
if (!redirect)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(redirect, sizeof(REDIRECTOR));
|
|
|
|
DWORD cchPath = wcslen(lpPath) + 1;
|
|
DWORD cchNewPath = wcslen(lpPathNew) + 1;
|
|
redirect->wzPath = (LPWSTR) malloc(cchPath * sizeof(WCHAR));
|
|
redirect->wzPathNew = (LPWSTR) malloc(cchNewPath * sizeof(WCHAR));
|
|
|
|
if (redirect->wzPath && redirect->wzPathNew)
|
|
{
|
|
HRESULT hr;
|
|
hr = StringCchCopyW(redirect->wzPath, cchPath, lpPath);
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
hr = StringCchCopyW(redirect->wzPathNew, cchNewPath, lpPathNew);
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
goto ErrorCleanup;
|
|
|
|
}
|
|
|
|
redirect->next = Redirectors;
|
|
Redirectors = redirect;
|
|
|
|
DPFN( eDbgLevelSpew, "Adding Redirector: %S ->\n %S", lpPath, lpPathNew);
|
|
|
|
return redirect;
|
|
|
|
ErrorCleanup:
|
|
free(redirect->wzPath);
|
|
free(redirect->wzPathNew);
|
|
free(redirect);
|
|
return NULL;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add a key protector to the virtual registry. See CheckProtected().
|
|
|
|
Arguments:
|
|
|
|
IN lpPath - Path to protector, eg: HKLM\Software\Microsoft
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
08/21/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
PROTECTOR *
|
|
CVirtualRegistry::AddKeyProtector(
|
|
IN LPCWSTR lpPath)
|
|
{
|
|
PROTECTOR *protect = (PROTECTOR *) malloc(sizeof(PROTECTOR));
|
|
|
|
if (!protect)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(protect, sizeof(PROTECTOR));
|
|
|
|
DWORD cchPath = wcslen(lpPath) + 1;
|
|
protect->wzPath = (LPWSTR) malloc(cchPath * sizeof(WCHAR));
|
|
|
|
if (protect->wzPath)
|
|
{
|
|
HRESULT hr;
|
|
hr = StringCchCopyW(protect->wzPath, cchPath, lpPath);
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
goto ErrorCleanup;
|
|
}
|
|
|
|
DPFN( eDbgLevelSpew, "Adding Key Protector: %S", lpPath);
|
|
protect->next = KeyProtectors;
|
|
KeyProtectors = protect;
|
|
|
|
return protect;
|
|
|
|
ErrorCleanup:
|
|
free(protect->wzPath);
|
|
free(protect);
|
|
return NULL;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Add an open key trigger to the virtual registry.
|
|
|
|
Arguments:
|
|
|
|
IN lpPath - Path to trigger on, eg: HKLM\Software\Microsoft
|
|
IN pfnOpenKey - Function to be called when key is opened.
|
|
|
|
Return Value:
|
|
|
|
New open key trigger, or NULL on failure.
|
|
|
|
History:
|
|
|
|
08/07/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
OPENKEYTRIGGER*
|
|
CVirtualRegistry::AddOpenKeyTrigger(
|
|
IN LPCWSTR lpPath,
|
|
IN _pfn_OpenKeyTrigger pfnOpenKey)
|
|
{
|
|
OPENKEYTRIGGER *openkeytrigger = (OPENKEYTRIGGER *) malloc(sizeof(OPENKEYTRIGGER));
|
|
|
|
if (!openkeytrigger)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return NULL;
|
|
}
|
|
|
|
ZeroMemory(openkeytrigger, sizeof(OPENKEYTRIGGER));
|
|
|
|
DWORD cchPath = wcslen(lpPath) + 1;
|
|
openkeytrigger->wzPath = (LPWSTR) malloc(cchPath * sizeof(WCHAR));
|
|
|
|
if (openkeytrigger->wzPath)
|
|
{
|
|
HRESULT hr = StringCchCopyW(openkeytrigger->wzPath, cchPath, lpPath);
|
|
if (FAILED(hr))
|
|
{
|
|
goto ErrorCleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
goto ErrorCleanup;
|
|
}
|
|
|
|
openkeytrigger->pfnTrigger = pfnOpenKey;
|
|
openkeytrigger->next = OpenKeyTriggers;
|
|
OpenKeyTriggers = openkeytrigger;
|
|
|
|
DPFN( eDbgLevelSpew, "Adding Open Key Trigger: %S, func@0x%x", lpPath, pfnOpenKey);
|
|
|
|
return openkeytrigger;
|
|
|
|
ErrorCleanup:
|
|
free(openkeytrigger->wzPath);
|
|
free(openkeytrigger);
|
|
return NULL;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Allow user to specify VRegistry.AddKey instead of VRegistry.Root->AddKey.
|
|
|
|
Arguments:
|
|
|
|
IN lpPath - Path of key
|
|
|
|
Return Value:
|
|
|
|
Virtual key
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
VIRTUALKEY *
|
|
CVirtualRegistry::AddKey(
|
|
IN LPCWSTR lpPath
|
|
)
|
|
{
|
|
return Root->AddKey(lpPath);
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Virtualized version of RegCreateKeyA, RegCreateKeyExA, RegOpenKeyA and RegOpenKeyExA
|
|
See RegOpenKey* and RegCreateKey* for details
|
|
|
|
Algorithm:
|
|
1. Convert lpSubKey and lpClass to WCHAR
|
|
2. Pass through to OpenKeyW
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Handle to open key or HKLM etc
|
|
IN lpSubKey - Subkey to open
|
|
IN lpClass - Address of a class string
|
|
IN DWORD dwOptions - special options flag
|
|
OUT phkResult - Handle to open key if successful
|
|
OUT lpdwDisposition - Address of disposition value buffer
|
|
IN bCreate - Create the key if it doesn't exist
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG CVirtualRegistry::OpenKeyA(
|
|
IN HKEY hKey,
|
|
IN LPCSTR lpSubKey,
|
|
IN LPSTR lpClass,
|
|
IN DWORD dwOptions,
|
|
IN REGSAM samDesired,
|
|
IN LPSECURITY_ATTRIBUTES pSecurityAttributes,
|
|
OUT HKEY *phkResult,
|
|
OUT LPDWORD lpdwDisposition,
|
|
IN BOOL bCreate
|
|
)
|
|
{
|
|
LONG lRet;
|
|
LPWSTR wzSubKey = NULL;
|
|
LPWSTR wzClass = NULL;
|
|
|
|
if (lpSubKey)
|
|
{
|
|
wzSubKey = ToUnicode(lpSubKey);
|
|
if (!wzSubKey)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
if (lpClass)
|
|
{
|
|
wzClass = ToUnicode(lpClass);
|
|
if (!wzClass)
|
|
{
|
|
free(wzSubKey);
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
lRet = OpenKeyW(
|
|
hKey,
|
|
wzSubKey,
|
|
wzClass,
|
|
dwOptions,
|
|
samDesired,
|
|
pSecurityAttributes,
|
|
phkResult,
|
|
lpdwDisposition,
|
|
bCreate,
|
|
FALSE,
|
|
NULL);
|
|
|
|
free(wzSubKey);
|
|
free(wzClass);
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegOpenKeyExW, RegOpenKeyW, RegCreateKeyW and RegCreateKeyExW
|
|
|
|
Algorithm:
|
|
1. Strip leading '\' characters
|
|
2. Inherit already open key data to get full key path
|
|
3. Redirect if necessary
|
|
4. RegOpenKeyEx with maximum possible security attributes
|
|
5. If the open failed, check for virtual key
|
|
6. If virtual, return a dummy key and succeed
|
|
7. Find the virtual key if it exists and attach it to the open key
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Handle to open key or HKLM etc
|
|
IN lpSubKey - Subkey to open
|
|
IN lpClass - Address of a class string
|
|
IN DWORD dwOptions - special options flag
|
|
OUT phkResult - Handle to open key if successful
|
|
OUT lpdwDisposition - Address of disposition value buffer
|
|
IN bCreate - Create the key if it doesn't exist
|
|
IN bRemote - Opening the remote registry.
|
|
IN lpMachineName - Machine name.
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::OpenKeyW(
|
|
IN HKEY hKey,
|
|
IN LPCWSTR lpSubKey,
|
|
IN LPWSTR lpClass,
|
|
IN DWORD dwOptions,
|
|
IN REGSAM samDesired,
|
|
IN LPSECURITY_ATTRIBUTES pSecurityAttributes,
|
|
OUT HKEY *phkResult,
|
|
OUT LPDWORD lpdwDisposition,
|
|
IN BOOL bCreate,
|
|
IN BOOL bRemote,
|
|
IN LPCWSTR lpMachineName
|
|
)
|
|
{
|
|
// Just a paranoid sanity check
|
|
if (!hKey)
|
|
{
|
|
DPFN( eDbgLevelError, "NULL handle passed to OpenKeyW");
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
// Hack for Mavis Beacon which uses really old stack for this parameter
|
|
if (lpdwDisposition && IsBadWritePtr(lpdwDisposition, sizeof(DWORD_PTR)))
|
|
{
|
|
DPFN( eDbgLevelError, "HACK: Ignoring bad lpdwDispostion pointer");
|
|
lpdwDisposition = NULL;
|
|
}
|
|
|
|
LONG lRet;
|
|
OPENKEY *key;
|
|
BOOL bVirtual, bRedirected;
|
|
VIRTUALKEY *vkey;
|
|
LPWSTR wzPath = NULL;
|
|
|
|
__try
|
|
{
|
|
// Base error condition
|
|
lRet = ERROR_INVALID_HANDLE;
|
|
|
|
// Everybody AVs if this ones bad
|
|
*phkResult = 0;
|
|
|
|
samDesired &= (KEY_WOW64_64KEY | KEY_WOW64_32KEY);
|
|
samDesired |= MAXIMUM_ALLOWED;
|
|
|
|
// Win9x ignores the options parameter
|
|
if (g_bWin9x)
|
|
{
|
|
if (dwOptions & REG_OPTION_VOLATILE)
|
|
{
|
|
LOGN( eDbgLevelWarning, "[OpenKeyW] Removing volatile flag");
|
|
}
|
|
dwOptions = REG_OPTION_NON_VOLATILE;
|
|
}
|
|
|
|
// Trim leading stuff, e.g. '\' character
|
|
lpSubKey = TrimSlashW(lpSubKey);
|
|
|
|
// Inherit from previously opened key
|
|
key = FindOpenKey(hKey);
|
|
if (key)
|
|
{
|
|
bVirtual = key->bVirtual;
|
|
bRedirected = key->bRedirected;
|
|
wzPath = MakePath(0, key->wzPath, lpSubKey);
|
|
}
|
|
else
|
|
{
|
|
bVirtual = FALSE;
|
|
bRedirected = FALSE;
|
|
wzPath = MakePath(hKey, NULL, lpSubKey);
|
|
}
|
|
|
|
if (!wzPath)
|
|
{
|
|
// Set the error code appropriately
|
|
lRet = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
// Check if we need to trigger on this key
|
|
else
|
|
{
|
|
CheckTriggers(wzPath);
|
|
}
|
|
|
|
// Now that we have the full path, see if we want to redirect it
|
|
if (!bRedirected && wzPath && CheckRedirect(&wzPath))
|
|
{
|
|
//
|
|
// Turn off virtual mode - since we don't know anything about the
|
|
// key we're redirecting to...
|
|
//
|
|
|
|
bVirtual = FALSE;
|
|
|
|
//
|
|
// Make sure we know we've been redirected so we don't get into recursive
|
|
// problems if the destination is a subkey of the source.
|
|
//
|
|
|
|
bRedirected = TRUE;
|
|
|
|
//
|
|
// We've been redirected, so we can no longer open the key directly:
|
|
// we have to get the full path in order to open the right key.
|
|
//
|
|
|
|
lpSubKey = SplitPath(wzPath, &hKey);
|
|
}
|
|
|
|
// Try and open the key if it's not already virtual
|
|
if (!bVirtual)
|
|
{
|
|
//
|
|
// Since we aren't virtual yet, we need to try for the original
|
|
// key. If one of these fail, then we'll go ahead and try for a
|
|
// virtual key.
|
|
//
|
|
|
|
if (bCreate)
|
|
{
|
|
lRet = ORIGINAL_API(RegCreateKeyExW)(
|
|
hKey,
|
|
lpSubKey,
|
|
0,
|
|
lpClass,
|
|
dwOptions,
|
|
samDesired,
|
|
pSecurityAttributes,
|
|
phkResult,
|
|
lpdwDisposition);
|
|
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
// Possible change in enumeration data, flush lists.
|
|
FlushEnumLists();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// bRemote is only true when this is called by the
|
|
// RegConnectRegistry hook so bCreate can't be true.
|
|
//
|
|
|
|
if (bRemote)
|
|
{
|
|
lRet = ORIGINAL_API(RegConnectRegistryW)(
|
|
lpMachineName,
|
|
hKey,
|
|
phkResult);
|
|
}
|
|
else
|
|
{
|
|
lRet = ORIGINAL_API(RegOpenKeyExW)(
|
|
hKey,
|
|
lpSubKey,
|
|
0,
|
|
samDesired,
|
|
phkResult);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have to look up the virtual key even if we managed to open an
|
|
// actual key, because when we query, we look for virtual values
|
|
// first. i.e. the virtual values override existing values.
|
|
//
|
|
|
|
vkey = Root->FindKey(wzPath);
|
|
|
|
// Check if our key is virtual, or may need to become virtual
|
|
if (bVirtual || FAILURE(lRet))
|
|
{
|
|
if (vkey)
|
|
{
|
|
//
|
|
// We have a virtual key, so create a dummy handle to hand back
|
|
// to the app.
|
|
//
|
|
|
|
*phkResult = CreateDummyKey();
|
|
|
|
if (*phkResult)
|
|
{
|
|
bVirtual = TRUE;
|
|
lRet = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// Couldn't create the dummy key, something seriously wrong.
|
|
DPFN(eDbgLevelError, "Couldn't create dummy key in OpenKeyW");
|
|
lRet = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if (SUCCESS(lRet) && wzPath)
|
|
{
|
|
// Made it this far, so make a new key entry
|
|
key = (OPENKEY *) malloc(sizeof(OPENKEY));
|
|
if (key)
|
|
{
|
|
key->vkey = vkey;
|
|
key->bVirtual = bVirtual;
|
|
key->bRedirected = bRedirected;
|
|
key->hkOpen = *phkResult;
|
|
key->wzPath = wzPath;
|
|
key->enumKeys = NULL;
|
|
key->enumValues = NULL;
|
|
key->next = OpenKeys;
|
|
OpenKeys = key;
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
|
|
// Clean up the dummy key
|
|
RegCloseKey(*phkResult);
|
|
|
|
lRet = ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
}
|
|
|
|
DPFN( ELEVEL(lRet), "%08lx=OpenKeyW(Key=%04lx)", lRet, hKey);
|
|
if (wzPath)
|
|
{
|
|
DPFN( ELEVEL(lRet), " Path=%S", wzPath);
|
|
}
|
|
DPFN( ELEVEL(lRet), " Result=%04lx", *phkResult);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DPFN( eDbgLevelError, "Exception occurred in OpenKeyW");
|
|
lRet = ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
if (FAILURE(lRet))
|
|
{
|
|
//
|
|
// If we failed for any reason, we didn't create an OPENKEY and so we
|
|
// can kill wzPath which was allocated by MakePath.
|
|
//
|
|
free(wzPath);
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegQueryValueExA and RegQueryValue.
|
|
See QueryValueW for more details.
|
|
|
|
Algorithm:
|
|
1. Call QueryValueW
|
|
2. If it's a string, convert back to ANSI
|
|
|
|
Note: this whole function is slightly more complex than it needs to be
|
|
because we don't want to query the value twice: once to get it's type
|
|
and the second time to get the value.
|
|
|
|
Most of the complications are due to the strings: we have to make sure we
|
|
have a buffer large enough so we can figure out how large the (possibly
|
|
DBCS) string is.
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Handle to open key
|
|
IN lpValueName - Value to query
|
|
IN lpType - Type of data, eg: REG_SZ
|
|
IN OUT lpData - Buffer for queries data
|
|
IN OUT lpcbData - Size of input buffer/size of returned data
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::QueryValueA(
|
|
IN HKEY hKey,
|
|
IN LPSTR lpValueName,
|
|
IN LPDWORD lpType,
|
|
IN OUT LPBYTE lpData,
|
|
IN OUT LPDWORD lpcbData
|
|
)
|
|
{
|
|
LONG lRet;
|
|
WCHAR wValueName[MAX_PATH];
|
|
DWORD dwType;
|
|
DWORD dwSize, dwOutSize;
|
|
LPBYTE lpBigData = NULL;
|
|
BOOL bText;
|
|
|
|
__try
|
|
{
|
|
// Can't have this
|
|
if (lpData && !lpcbData)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// Convert the Value Name to WCHAR
|
|
if (lpValueName)
|
|
{
|
|
if (MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
lpValueName,
|
|
-1,
|
|
(LPWSTR)wValueName,
|
|
MAX_PATH) == 0)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wValueName[0] = L'\0';
|
|
}
|
|
|
|
//
|
|
// Get an initial size to use: if they sent us a buffer, we start with
|
|
// that size, otherwise, we try a reasonable string length
|
|
//
|
|
|
|
if (lpData && *lpcbData)
|
|
{
|
|
dwSize = *lpcbData;
|
|
}
|
|
else
|
|
{
|
|
dwSize = MAX_PATH;
|
|
}
|
|
|
|
Retry:
|
|
//
|
|
// We can't touch their buffer unless we're going to succeed, so we
|
|
// have to double buffer the call.
|
|
//
|
|
|
|
lpBigData = (LPBYTE) malloc(dwSize);
|
|
|
|
if (!lpBigData)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
lRet = QueryValueW(hKey, wValueName, &dwType, lpBigData, &dwSize);
|
|
|
|
//
|
|
// We need to know if it's a string, since then we have to do extra
|
|
// work to calculate the real size of the buffer etc.
|
|
//
|
|
|
|
bText = (SUCCESS(lRet) || (lRet == ERROR_MORE_DATA)) &&
|
|
((dwType == REG_SZ) ||
|
|
(dwType == REG_EXPAND_SZ) ||
|
|
(dwType == REG_MULTI_SZ));
|
|
|
|
if (bText && (lRet == ERROR_MORE_DATA))
|
|
{
|
|
//
|
|
// The buffer wasn't big enough: we have to actually query the value
|
|
// so we can get the real length in case it's DBCS, so we retry.
|
|
// Note: dwSize now contains the required size, so it will succeed
|
|
// this time around.
|
|
//
|
|
|
|
free(lpBigData);
|
|
|
|
goto Retry;
|
|
}
|
|
|
|
//
|
|
// Calculate the size of the output buffer: if it's text, it may be
|
|
// a DBCS string, so we need to get the right size
|
|
//
|
|
|
|
if (bText)
|
|
{
|
|
dwOutSize = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
(LPWSTR) lpBigData,
|
|
dwSize / sizeof(WCHAR),
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
// It's not text, so we just use the actual size
|
|
dwOutSize = dwSize;
|
|
}
|
|
|
|
//
|
|
// If they gave us a buffer, we fill it in with what we got back
|
|
//
|
|
|
|
if (SUCCESS(lRet) && lpData)
|
|
{
|
|
//
|
|
// Make sure we have enough space: lpcbData is guaranteed to be
|
|
// valid since lpData is ok.
|
|
//
|
|
|
|
if (*lpcbData >= dwOutSize)
|
|
{
|
|
if (bText)
|
|
{
|
|
//
|
|
// Convert the string back to ANSI. The buffer must have been big
|
|
// enough since QueryValue succeeded.
|
|
// Note: we have to give the exact size to convert otherwise we
|
|
// use more of the buffer than absolutely necessary. Some apps,
|
|
// like NHL 98 say they have a 256 byte buffer, but only give us
|
|
// a 42 byte buffer. On NT, everything is done in place on that
|
|
// buffer: so we always use more than the exact string length.
|
|
// This shim gets around that because we use separate buffers.
|
|
//
|
|
|
|
if (WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
(LPWSTR)lpBigData,
|
|
dwSize / 2,
|
|
(LPSTR)lpData,
|
|
dwOutSize, // *lpcbData,
|
|
0,
|
|
0) == 0)
|
|
{
|
|
free(lpBigData);
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MoveMemory(lpData, lpBigData, dwSize);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lRet = ERROR_MORE_DATA;
|
|
}
|
|
}
|
|
|
|
free(lpBigData);
|
|
|
|
// Fill the output structures in if possible
|
|
if (lpType)
|
|
{
|
|
*lpType = dwType;
|
|
}
|
|
|
|
if (lpcbData)
|
|
{
|
|
*lpcbData = dwOutSize;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DPFN( eDbgLevelError, "Exception occurred in QueryValueA");
|
|
lRet = ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegQueryValueExW and RegQueryValue. We first see if the value
|
|
is virtual because virtual values override actual values
|
|
|
|
Algorithm:
|
|
1. Check if it's a virtual value and if so, spoof it
|
|
2. If it's not virtual, query registry normally
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Handle to open key
|
|
IN lpValueName - Value to query
|
|
IN lpType - Type of data, eg: REG_SZ
|
|
IN OUT lpData - Buffer for queries data
|
|
IN OUT lpcbData - Size of input buffer/size of returned data
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::QueryValueW(
|
|
IN HKEY hKey,
|
|
IN LPWSTR lpValueName,
|
|
IN LPDWORD lpType,
|
|
IN OUT LPBYTE lpData,
|
|
IN OUT LPDWORD lpcbData
|
|
)
|
|
{
|
|
// Just a paranoid sanity check
|
|
if (!hKey)
|
|
{
|
|
DPFN( eDbgLevelError, "NULL handle passed to OpenKeyW");
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
LONG lRet;
|
|
OPENKEY *key;
|
|
VIRTUALKEY *vkey;
|
|
VIRTUALVAL *vvalue;
|
|
DWORD dwType;
|
|
WCHAR* lpBuffer;
|
|
DWORD dwStringSize;
|
|
DWORD cbData = 0;
|
|
BOOL bDataPresent = TRUE;
|
|
|
|
__try
|
|
{
|
|
lRet = ERROR_FILE_NOT_FOUND;
|
|
|
|
// Can't have this
|
|
if (lpData && !lpcbData)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
// We always need the type
|
|
if (!lpType)
|
|
{
|
|
lpType = &dwType;
|
|
}
|
|
|
|
// Do we want to spoof this
|
|
key = FindOpenKey(hKey);
|
|
vkey = key ? key->vkey : NULL;
|
|
vvalue = vkey ? vkey->FindValue(lpValueName) : NULL;
|
|
|
|
if (key && vkey && vvalue &&
|
|
(vvalue->cbData != 0 || vvalue->pfnQueryValue))
|
|
{
|
|
// Use the callback if available
|
|
if (vvalue->pfnQueryValue)
|
|
{
|
|
//
|
|
// Note, the callback puts it's values into the vvalue field,
|
|
// just as if we knew it all along. In addition, we can fail
|
|
// the call... but that doesn't allow us defer to the original
|
|
// value.
|
|
//
|
|
|
|
lRet = (*vvalue->pfnQueryValue)(
|
|
key,
|
|
vkey,
|
|
vvalue);
|
|
}
|
|
else
|
|
{
|
|
lRet = ERROR_SUCCESS;
|
|
}
|
|
|
|
// Copy the virtual value into the buffer
|
|
if (SUCCESS(lRet))
|
|
{
|
|
*lpType = vvalue->dwType;
|
|
|
|
if (lpData)
|
|
{
|
|
if (vvalue->cbData <= *lpcbData)
|
|
{
|
|
MoveMemory(lpData, vvalue->lpData, vvalue->cbData);
|
|
}
|
|
else
|
|
{
|
|
lRet = ERROR_MORE_DATA;
|
|
}
|
|
}
|
|
|
|
if (lpcbData)
|
|
{
|
|
*lpcbData = vvalue->cbData;
|
|
}
|
|
}
|
|
}
|
|
else if (key && vkey && vvalue &&
|
|
(vvalue->cbData == 0))
|
|
{
|
|
bDataPresent = FALSE;
|
|
lRet = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
// Save the size of the data buffer.
|
|
if (lpcbData)
|
|
{
|
|
cbData = *lpcbData;
|
|
}
|
|
|
|
//
|
|
// Get the key normally as if it weren't virtual at all
|
|
//
|
|
|
|
lRet = ORIGINAL_API(RegQueryValueExW)(
|
|
hKey,
|
|
lpValueName,
|
|
NULL,
|
|
lpType,
|
|
lpData,
|
|
lpcbData);
|
|
|
|
//
|
|
// Some apps store bogus data beyond the end of the string.
|
|
// Attempt to fix.
|
|
//
|
|
|
|
// Only try this if it's a string.
|
|
if (lRet == ERROR_MORE_DATA && (*lpType == REG_SZ || *lpType == REG_EXPAND_SZ))
|
|
{
|
|
//
|
|
// Create a buffer large enough to hold the data
|
|
// We read from lpcbData here, but this should be ok,
|
|
// since RegQueryValueEx shouldn't return ERROR_MORE_DATA
|
|
// if lpcbData is NULL.
|
|
//
|
|
lpBuffer = (WCHAR*)malloc(*lpcbData);
|
|
if (lpBuffer)
|
|
{
|
|
// Requery with new buffer.
|
|
lRet = ORIGINAL_API(RegQueryValueExW)(
|
|
hKey,
|
|
lpValueName,
|
|
NULL,
|
|
lpType,
|
|
(BYTE*)lpBuffer,
|
|
lpcbData);
|
|
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
dwStringSize = wcslen(lpBuffer)*sizeof(WCHAR) + sizeof(WCHAR);
|
|
// If size of dest buffer can hold the string . . .
|
|
if (cbData >= dwStringSize)
|
|
{
|
|
DPFN(eDbgLevelInfo, "\tTrimming data beyond end of string in Query for %S", lpValueName);
|
|
|
|
// Copy the data to the caller's buffer,
|
|
CopyMemory(lpData, lpBuffer, dwStringSize);
|
|
|
|
*lpcbData = dwStringSize;
|
|
}
|
|
else
|
|
{
|
|
// Set *lpcbData to the correct size, and return more data error
|
|
*lpcbData = dwStringSize;
|
|
|
|
lRet = ERROR_MORE_DATA;
|
|
}
|
|
}
|
|
|
|
free(lpBuffer);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Here's another hack for us: if the value is NULL or an empty string
|
|
// Win9x defers to QueryValue...
|
|
//
|
|
|
|
if (g_bWin9x && (lRet == ERROR_FILE_NOT_FOUND) &&
|
|
(!lpValueName || !lpValueName[0]))
|
|
{
|
|
lRet = ORIGINAL_API(RegQueryValueW)(
|
|
hKey,
|
|
NULL,
|
|
(LPWSTR)lpData,
|
|
(PLONG)lpcbData);
|
|
|
|
if (SUCCESS(lRet))
|
|
{
|
|
*lpType = REG_SZ;
|
|
}
|
|
}
|
|
}
|
|
|
|
DPFN( ELEVEL(lRet), "%08lx=QueryValueW(Key=%04lx)",
|
|
lRet,
|
|
hKey);
|
|
|
|
if (key)
|
|
{
|
|
DPFN( ELEVEL(lRet), " Path=%S\\%S", key->wzPath, lpValueName);
|
|
}
|
|
else
|
|
{
|
|
DPFN( ELEVEL(lRet), " Value=%S", lpValueName);
|
|
}
|
|
|
|
if (SUCCESS(lRet) &&
|
|
((*lpType == REG_SZ) ||
|
|
(*lpType == REG_EXPAND_SZ))&&
|
|
(bDataPresent == TRUE))
|
|
{
|
|
DPFN( eDbgLevelInfo, " Result=%S", lpData);
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DPFN( eDbgLevelError, "Exception occurred in QueryValueW");
|
|
lRet = ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegEnumKeyA
|
|
Call out to EnumKeyW and convert the name back to ANSI. Note we pass the
|
|
size given to us (in lpcbName) down to EnumKeyW in case the lpName buffer
|
|
is too small.
|
|
|
|
Algoritm:
|
|
1. EnumKeyW with a large buffer
|
|
2. Convert the key back to ansi if it succeeds
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Handle to open key
|
|
IN dwIndex - Index to enumerate
|
|
OUT lpName - Name of subkey
|
|
IN OUT lpcbName - Size of name buffer
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::EnumKeyA(
|
|
IN HKEY hKey,
|
|
IN DWORD dwIndex,
|
|
OUT LPSTR lpName,
|
|
OUT LPDWORD lpcbName
|
|
)
|
|
{
|
|
LONG lRet = ERROR_NO_MORE_ITEMS;
|
|
WCHAR wKey[MAX_PATH + 1];
|
|
DWORD dwcbName = MAX_PATH + 1;
|
|
|
|
__try
|
|
{
|
|
lRet = EnumKeyW(hKey, dwIndex, (LPWSTR)wKey, &dwcbName);
|
|
|
|
if (SUCCESS(lRet))
|
|
{
|
|
DWORD dwByte = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
(LPWSTR)wKey,
|
|
dwcbName,
|
|
(LPSTR)lpName,
|
|
*lpcbName,
|
|
0,
|
|
0);
|
|
|
|
lpName[dwByte] = '\0';
|
|
*lpcbName = dwByte;
|
|
if (!dwByte)
|
|
{
|
|
lRet = GetLastError();
|
|
|
|
// Generate a registry error code
|
|
if (lRet == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
lRet = ERROR_MORE_DATA;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
lRet = ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegEnumKeyW.
|
|
|
|
Algorithm:
|
|
1. Build enumeration list, if necessary.
|
|
2. Iterate through enumeration list until index is found.
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Handle to open key
|
|
IN dwIndex - Index to enumerate
|
|
OUT lpName - Name of subkey
|
|
OUT lpcbName - Size of name buffer
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::EnumKeyW(
|
|
HKEY hKey,
|
|
DWORD dwIndex,
|
|
LPWSTR lpName,
|
|
LPDWORD lpcbName
|
|
)
|
|
{
|
|
LONG lRet = ERROR_BAD_ARGUMENTS;
|
|
OPENKEY *key;
|
|
ENUMENTRY *enumkey;
|
|
DWORD i;
|
|
|
|
__try
|
|
{
|
|
key = FindOpenKey(hKey);
|
|
if (key)
|
|
{
|
|
if (key->enumKeys == NULL)
|
|
{
|
|
key->BuildEnumList();
|
|
}
|
|
|
|
i = 0;
|
|
enumkey = key->enumKeys;
|
|
while (enumkey)
|
|
{
|
|
if (dwIndex == i)
|
|
{
|
|
DWORD len = wcslen(enumkey->wzName);
|
|
|
|
if (*lpcbName > len)
|
|
{
|
|
HRESULT hr;
|
|
hr = StringCchCopyW(lpName, *lpcbName, enumkey->wzName);
|
|
if (FAILED(hr))
|
|
{
|
|
lRet = ERROR_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
*lpcbName = len;
|
|
lRet = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lRet = ERROR_MORE_DATA;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
i++;
|
|
enumkey = enumkey->next;
|
|
}
|
|
|
|
// No key found for index
|
|
if (enumkey == NULL)
|
|
{
|
|
lRet = ERROR_NO_MORE_ITEMS;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
lRet = ORIGINAL_API(RegEnumKeyExW)(
|
|
hKey,
|
|
dwIndex,
|
|
lpName,
|
|
lpcbName,
|
|
0,
|
|
0,
|
|
0,
|
|
0);
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
lRet = ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
DPFN( ELEVEL(lRet), "%08lx=EnumKeyW(hKey=%04lx,dwIndex=%d)",
|
|
lRet,
|
|
hKey,
|
|
dwIndex);
|
|
|
|
if (SUCCESS(lRet))
|
|
{
|
|
DPFN( eDbgLevelInfo, " Result=%S", lpName);
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegEnumValueA. Thunks to QueryValueW.
|
|
This function calls QueryValueA to get the data
|
|
out of the value, so most error handling is done by QueryValueA.
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Handle to open key
|
|
IN dwIndex - Index of value to enumerate
|
|
IN OUT lpValueName - Value name buffer
|
|
IN OUT lpcbValueName - Sizeof value name buffer
|
|
IN OUT lpType - Type of data, eg: REG_SZ
|
|
IN OUT lpData - Buffer for queries data
|
|
IN OUT lpcbData - Size of input buffer/size of returned data
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::EnumValueA(
|
|
IN HKEY hKey,
|
|
IN DWORD dwIndex,
|
|
IN OUT LPSTR lpValueName,
|
|
IN OUT LPDWORD lpcbValueName,
|
|
IN OUT LPDWORD lpType,
|
|
IN OUT LPBYTE lpData,
|
|
IN OUT LPDWORD lpcbData
|
|
)
|
|
{
|
|
LONG lRet;
|
|
WCHAR wzValueName[MAX_PATH];
|
|
DWORD dwValNameSize;
|
|
|
|
|
|
__try
|
|
{
|
|
dwValNameSize = MAX_PATH;
|
|
lRet = EnumValueW(hKey, dwIndex, wzValueName, &dwValNameSize, NULL, NULL, NULL);
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
dwValNameSize = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
wzValueName,
|
|
-1,
|
|
lpValueName,
|
|
*lpcbValueName,
|
|
NULL,
|
|
NULL);
|
|
if (dwValNameSize != 0)
|
|
{
|
|
// Just do a normal query value for the remaining parameters.
|
|
lRet = QueryValueA(hKey, lpValueName, lpType, lpData, lpcbData);
|
|
}
|
|
else
|
|
{
|
|
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
|
|
{
|
|
lRet = ERROR_MORE_DATA;
|
|
*lpcbValueName = WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
wzValueName,
|
|
-1,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
else
|
|
{
|
|
lRet = GetLastError();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
lRet = ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegEnumValueW. This function calls QueryValueW to get the data
|
|
out of the value, so most error handling is done by QueryValueW.
|
|
|
|
Algorithm:
|
|
1. Check if key has virtual values, if not default to RegEnumValueW.
|
|
2. Build enumeration list, if necessary.
|
|
3. Iterate through enumeration list until index is found.
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Handle to open key
|
|
IN dwIndex - Index of value to enumerate
|
|
IN OUT lpValueName - Value name buffer
|
|
IN OUT lpcbValueName - Sizeof value name buffer
|
|
IN OUT lpType - Type of data, eg: REG_SZ
|
|
IN OUT lpData - Buffer for queries data
|
|
IN OUT lpcbData - Size of input buffer/size of returned data
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::EnumValueW(
|
|
IN HKEY hKey,
|
|
IN DWORD dwIndex,
|
|
IN OUT LPWSTR lpValueName,
|
|
IN OUT LPDWORD lpcbValueName,
|
|
IN OUT LPDWORD lpType,
|
|
IN OUT LPBYTE lpData,
|
|
IN OUT LPDWORD lpcbData
|
|
)
|
|
{
|
|
LONG lRet;
|
|
OPENKEY *key;
|
|
ENUMENTRY *enumval;
|
|
|
|
// Check if it has virtual values . . .
|
|
key = FindOpenKey(hKey);
|
|
if (key && key->vkey && key->vkey->values)
|
|
{
|
|
DWORD i = 0;
|
|
if (key->enumValues == NULL)
|
|
{
|
|
key->BuildEnumList();
|
|
}
|
|
|
|
enumval = key->enumValues;
|
|
|
|
lRet = ERROR_NO_MORE_ITEMS;
|
|
|
|
while (enumval)
|
|
{
|
|
if (dwIndex == i)
|
|
{
|
|
DWORD len = wcslen(enumval->wzName);
|
|
|
|
if (*lpcbValueName > len)
|
|
{
|
|
// Copy the name and query the data
|
|
HRESULT hr = StringCchCopyW(lpValueName, *lpcbValueName, enumval->wzName);
|
|
if (FAILED(hr))
|
|
{
|
|
lRet = ERROR_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
*lpcbValueName = len;
|
|
lRet = QueryValueW(
|
|
hKey,
|
|
enumval->wzName,
|
|
lpType,
|
|
lpData,
|
|
lpcbData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// The buffer given for name wasn't big enough
|
|
lRet = ERROR_MORE_DATA;
|
|
}
|
|
|
|
break;
|
|
}
|
|
i++;
|
|
enumval = enumval->next;
|
|
}
|
|
}
|
|
// No virtual values, fall through to original API.
|
|
else
|
|
{
|
|
lRet = ORIGINAL_API(RegEnumValueW)(
|
|
hKey,
|
|
dwIndex,
|
|
lpValueName,
|
|
lpcbValueName,
|
|
0,
|
|
lpType,
|
|
lpData,
|
|
lpcbData);
|
|
}
|
|
|
|
DPFN( ELEVEL(lRet), "%08lx=EnumValueW(hKey=%04lx,dwIndex=%d)",
|
|
lRet,
|
|
hKey,
|
|
dwIndex);
|
|
|
|
if (SUCCESS(lRet))
|
|
{
|
|
DPFN( eDbgLevelInfo, " Result=%S", lpValueName);
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegQueryInfoKeyA.
|
|
We don't need to worry about the conversion of ansi->unicode in the sizes
|
|
of values and keys because they are defined as string lengths.
|
|
|
|
Algorithm:
|
|
1. Convert the class string to unicode
|
|
2. Call QueryInfoW
|
|
|
|
|
|
Arguments:
|
|
|
|
IN hKey - handle to key to query
|
|
OUT lpClass - address of buffer for class string
|
|
OUT lpcbClass - address of size of class string buffer
|
|
OUT lpReserved - reserved
|
|
OUT lpcSubKeys - address of buffer for number of subkeys
|
|
OUT lpcbMaxSubKeyLen - address of buffer for longest subkey
|
|
OUT lpcbMaxClassLen - address of buffer for longest class string length
|
|
OUT lpcValues - address of buffer for number of value entries
|
|
OUT lpcbMaxValueNameLen - address of buffer for longest value name length
|
|
OUT lpcbMaxValueLen - address of buffer for longest value data length
|
|
OUT lpcbSecurityDescriptor - address of buffer for security descriptor length
|
|
OUT lpftLastWriteTime - address of buffer for last write time
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::QueryInfoA(
|
|
IN HKEY hKey,
|
|
OUT LPSTR lpClass,
|
|
OUT LPDWORD lpcbClass,
|
|
OUT LPDWORD lpReserved,
|
|
OUT LPDWORD lpcSubKeys,
|
|
OUT LPDWORD lpcbMaxSubKeyLen,
|
|
OUT LPDWORD lpcbMaxClassLen,
|
|
OUT LPDWORD lpcValues,
|
|
OUT LPDWORD lpcbMaxValueNameLen,
|
|
OUT LPDWORD lpcbMaxValueLen,
|
|
OUT LPDWORD lpcbSecurityDescriptor,
|
|
OUT PFILETIME lpftLastWriteTime
|
|
)
|
|
{
|
|
LONG lRet;
|
|
|
|
if (lpClass && !lpcbClass)
|
|
{
|
|
LOGN( eDbgLevelError, "[QueryInfoA] NULL passed for lpClass but not lpcbClass. Fixing.");
|
|
lpcbClass = NULL;
|
|
}
|
|
|
|
if (lpClass && lpcbClass)
|
|
{
|
|
WCHAR wClass[MAX_PATH];
|
|
DWORD dwSize;
|
|
|
|
if (MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
lpClass,
|
|
-1,
|
|
(LPWSTR)wClass,
|
|
MAX_PATH) == 0)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
dwSize = *lpcbClass * 2;
|
|
|
|
lRet = QueryInfoW(
|
|
hKey,
|
|
wClass,
|
|
&dwSize,
|
|
lpReserved,
|
|
lpcSubKeys,
|
|
lpcbMaxSubKeyLen,
|
|
lpcbMaxClassLen,
|
|
lpcValues,
|
|
lpcbMaxValueNameLen,
|
|
lpcbMaxValueLen,
|
|
lpcbSecurityDescriptor,
|
|
lpftLastWriteTime);
|
|
|
|
|
|
if (SUCCESS(lRet))
|
|
{
|
|
if (WideCharToMultiByte(
|
|
CP_ACP,
|
|
0,
|
|
(LPWSTR)wClass,
|
|
dwSize,
|
|
(LPSTR)lpClass,
|
|
*lpcbClass,
|
|
0,
|
|
0) == 0)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
*lpcbClass = dwSize / 2;
|
|
}
|
|
else
|
|
{
|
|
lRet = QueryInfoW(
|
|
hKey,
|
|
NULL,
|
|
NULL,
|
|
lpReserved,
|
|
lpcSubKeys,
|
|
lpcbMaxSubKeyLen,
|
|
lpcbMaxClassLen,
|
|
lpcValues,
|
|
lpcbMaxValueNameLen,
|
|
lpcbMaxValueLen,
|
|
lpcbSecurityDescriptor,
|
|
lpftLastWriteTime);
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegQueryInfoKeyW.
|
|
|
|
Algorithm:
|
|
1. Revert to the old API if the key isn't virtual
|
|
2. Calculate all the virtual key and value name lengths by going through
|
|
them individually.
|
|
3. Add all non-virtual key and value's that don't have overriding virtual's.
|
|
|
|
Arguments:
|
|
|
|
IN hKey - handle to key to query
|
|
OUT lpClass - address of buffer for class string
|
|
OUT lpcbClass - address of size of class string buffer
|
|
OUT lpReserved - reserved
|
|
OUT lpcSubKeys - address of buffer for number of subkeys
|
|
OUT lpcbMaxSubKeyLen - address of buffer for longest subkey
|
|
OUT lpcbMaxClassLen - address of buffer for longest class string length
|
|
OUT lpcValues - address of buffer for number of value entries
|
|
OUT lpcbMaxValueNameLen - address of buffer for longest value name length
|
|
OUT lpcbMaxValueLen - address of buffer for longest value data length
|
|
OUT lpcbSecurityDescriptor - address of buffer for security descriptor length
|
|
OUT lpftLastWriteTime - address of buffer for last write time
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
08/03/2001 mikrause Added support for counting both virtual & non-virtual keys & values.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::QueryInfoW(
|
|
IN HKEY hKey,
|
|
OUT LPWSTR lpClass,
|
|
OUT LPDWORD lpcbClass,
|
|
OUT LPDWORD lpReserved,
|
|
OUT LPDWORD lpcSubKeys,
|
|
OUT LPDWORD lpcbMaxSubKeyLen,
|
|
OUT LPDWORD lpcbMaxClassLen,
|
|
OUT LPDWORD lpcValues,
|
|
OUT LPDWORD lpcbMaxValueNameLen,
|
|
OUT LPDWORD lpcbMaxValueLen,
|
|
OUT LPDWORD lpcbSecurityDescriptor,
|
|
OUT PFILETIME lpftLastWriteTime
|
|
)
|
|
{
|
|
LONG lRet;
|
|
OPENKEY *key;
|
|
|
|
DWORD cbData = 0;
|
|
ENUMENTRY *enumkey;
|
|
ENUMENTRY *enumval;
|
|
|
|
if (lpClass && !lpcbClass)
|
|
{
|
|
LOGN( eDbgLevelError, "[QueryInfoW] NULL passed for lpClass but not lpcbClass. Fixing.");
|
|
lpcbClass = NULL;
|
|
}
|
|
|
|
key = FindOpenKey(hKey);
|
|
if (key)
|
|
{
|
|
if (lpClass)
|
|
{
|
|
lpClass[0] = L'\0';
|
|
}
|
|
|
|
if (lpcbClass)
|
|
{
|
|
*lpcbClass = 0;
|
|
}
|
|
|
|
if (lpcbMaxClassLen)
|
|
{
|
|
*lpcbMaxClassLen = 0;
|
|
}
|
|
|
|
if (lpReserved)
|
|
{
|
|
*lpReserved = 0;
|
|
}
|
|
|
|
if (lpcSubKeys || lpcbMaxSubKeyLen)
|
|
{
|
|
DWORD i = 0;
|
|
DWORD len = 0;
|
|
|
|
// Count virtual keys.
|
|
if (!key->enumKeys)
|
|
{
|
|
key->BuildEnumList();
|
|
}
|
|
|
|
enumkey = key->enumKeys;
|
|
while (enumkey)
|
|
{
|
|
i++;
|
|
len = max(len, wcslen(enumkey->wzName));
|
|
enumkey = enumkey->next;
|
|
}
|
|
|
|
if (lpcSubKeys)
|
|
{
|
|
*lpcSubKeys = i;
|
|
}
|
|
if (lpcbMaxSubKeyLen)
|
|
{
|
|
*lpcbMaxSubKeyLen = len;
|
|
}
|
|
}
|
|
|
|
if (lpcValues || lpcbMaxValueNameLen || lpcbMaxValueLen)
|
|
{
|
|
// Check if this key has virtual values or is virtual.
|
|
if (key->bVirtual || (key->vkey && key->vkey->values))
|
|
{
|
|
DWORD i = 0;
|
|
DWORD lenA = 0, lenB = 0;
|
|
|
|
if (key->enumValues == NULL)
|
|
{
|
|
key->BuildEnumList();
|
|
}
|
|
|
|
enumval = key->enumValues;
|
|
while (enumval)
|
|
{
|
|
i++;
|
|
QueryValueW(key->hkOpen, enumval->wzName, NULL, NULL, &cbData);
|
|
|
|
lenA = max(lenA, cbData);
|
|
lenB = max(lenB, wcslen(enumval->wzName));
|
|
enumval = enumval->next;
|
|
}
|
|
|
|
if (lpcValues)
|
|
{
|
|
*lpcValues = i;
|
|
}
|
|
if (lpcbMaxValueLen)
|
|
{
|
|
*lpcbMaxValueLen = lenA;
|
|
}
|
|
if (lpcbMaxValueNameLen)
|
|
{
|
|
*lpcbMaxValueNameLen = lenB;
|
|
}
|
|
}
|
|
// No virtual values, do a normal query.
|
|
else
|
|
{
|
|
lRet = ORIGINAL_API(RegQueryInfoKeyW)(
|
|
key->hkOpen,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
lpcValues,
|
|
lpcbMaxValueNameLen,
|
|
lpcbMaxValueLen,
|
|
NULL,
|
|
lpftLastWriteTime);
|
|
|
|
}
|
|
}
|
|
if (lpcbSecurityDescriptor)
|
|
{
|
|
*lpcbSecurityDescriptor = NULL;
|
|
}
|
|
|
|
lRet = ERROR_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
lRet = ORIGINAL_API(RegQueryInfoKeyW)(
|
|
hKey,
|
|
lpClass,
|
|
lpcbClass,
|
|
lpReserved,
|
|
lpcSubKeys,
|
|
lpcbMaxSubKeyLen,
|
|
lpcbMaxClassLen,
|
|
lpcValues,
|
|
lpcbMaxValueNameLen,
|
|
lpcbMaxValueLen,
|
|
lpcbSecurityDescriptor,
|
|
lpftLastWriteTime);
|
|
}
|
|
|
|
DPFN( ELEVEL(lRet), "%08lx=QueryInfoW(hKey=%04lx)",
|
|
lRet,
|
|
hKey);
|
|
|
|
if (key)
|
|
{
|
|
DPFN( ELEVEL(lRet), " Path=%S", key->wzPath);
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegSetValueA.
|
|
|
|
Algorithm:
|
|
1. Convert value name and data (if string) to Unicode.
|
|
2. Call SetValueW
|
|
|
|
Arguments:
|
|
|
|
hKey - Key to set value in.
|
|
lpValueName - Name of value to set.
|
|
dwType - Type of value (string, DWORD, etc.)
|
|
lpData - Buffer containing data to write.
|
|
cbData - Size of lpData in bytes.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS on success, failure code otherwise.
|
|
|
|
History:
|
|
|
|
08/07/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::SetValueA(
|
|
HKEY hKey,
|
|
LPCSTR lpValueName,
|
|
DWORD dwType,
|
|
CONST BYTE* lpData,
|
|
DWORD cbData
|
|
)
|
|
{
|
|
LONG lRet;
|
|
DWORD dwSize;
|
|
WCHAR* wszValName = NULL;
|
|
BYTE* lpExpandedData = (BYTE*)lpData;
|
|
|
|
if (lpValueName != NULL)
|
|
{
|
|
dwSize = (DWORD)(lstrlenA(lpValueName) + 1);
|
|
dwSize *= sizeof(WCHAR);
|
|
wszValName = (WCHAR*)malloc(dwSize);
|
|
if (wszValName == NULL)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
lpValueName,
|
|
-1,
|
|
(LPWSTR)wszValName,
|
|
dwSize/sizeof(WCHAR)) == 0)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
dwSize = cbData;
|
|
|
|
//
|
|
// Expand text buffers
|
|
//
|
|
if (lpData && (dwType == REG_SZ || dwType == REG_EXPAND_SZ || dwType == REG_MULTI_SZ))
|
|
{
|
|
if ((dwType != REG_MULTI_SZ) && g_bWin9x)
|
|
{
|
|
dwSize = (DWORD)(lstrlenA((char*)lpData) + 1);
|
|
}
|
|
|
|
lpExpandedData = (BYTE*) malloc(dwSize * sizeof(WCHAR));
|
|
if (lpExpandedData == NULL)
|
|
{
|
|
if (wszValName)
|
|
{
|
|
free(wszValName);
|
|
}
|
|
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
(LPCSTR)lpData,
|
|
dwSize,
|
|
(LPWSTR)lpExpandedData,
|
|
dwSize) == 0)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
dwSize = dwSize * sizeof(WCHAR);
|
|
}
|
|
|
|
lRet = SetValueW(hKey, wszValName, dwType, lpExpandedData, dwSize);
|
|
|
|
if (lpExpandedData != lpData)
|
|
{
|
|
free(lpExpandedData);
|
|
}
|
|
|
|
if (wszValName)
|
|
{
|
|
free(wszValName);
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegSetValueW.
|
|
Also protects for non-zero buffer length with zero buffer AV.
|
|
|
|
Algorithm:
|
|
1. If non-protected key, write to registry using RegSetValueW
|
|
|
|
Arguments:
|
|
|
|
hKey - Key to set value in.
|
|
lpValueName - Name of value to set.
|
|
dwType - Type of value (string, DWORD, etc.)
|
|
lpData - Buffer containing data to write.
|
|
cbData - Size of lpData in bytes.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS on success, failure code otherwise.
|
|
|
|
History:
|
|
|
|
08/07/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::SetValueW(
|
|
HKEY hKey,
|
|
LPCWSTR lpValueName,
|
|
DWORD dwType,
|
|
CONST BYTE* lpData,
|
|
DWORD cbData
|
|
)
|
|
{
|
|
LONG lRet;
|
|
|
|
// Just a paranoid sanity check
|
|
if (!hKey)
|
|
{
|
|
DPFN( eDbgLevelError, "NULL handle passed to SetValueW");
|
|
return ERROR_INVALID_HANDLE;
|
|
}
|
|
__try
|
|
{
|
|
lRet = ERROR_FILE_NOT_FOUND;
|
|
|
|
// To duplicate Win95/win98 behavior automatically override
|
|
// the cbData with the actual length of the lpData for REG_SZ.
|
|
if (g_bWin9x && lpData &&
|
|
((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)))
|
|
{
|
|
cbData = (wcslen((WCHAR *)lpData)+1)*sizeof(WCHAR);
|
|
}
|
|
|
|
VIRTUALKEY *vkey;
|
|
VIRTUALVAL *vvalue;
|
|
OPENKEY* key = FindOpenKey(hKey);
|
|
if (key)
|
|
{
|
|
// Check if we should execute a custom action.
|
|
vkey = key->vkey;
|
|
vvalue = vkey ? vkey->FindValue(lpValueName) : NULL;
|
|
if (vkey && vvalue &&
|
|
vvalue->pfnSetValue)
|
|
{
|
|
lRet = vvalue->pfnSetValue(key, vkey, vvalue,
|
|
dwType, lpData,cbData);
|
|
}
|
|
else
|
|
{
|
|
// No custom action, just set value as normal.
|
|
lRet = ORIGINAL_API(RegSetValueExW)(
|
|
hKey,
|
|
lpValueName,
|
|
0,
|
|
dwType,
|
|
lpData,
|
|
cbData);
|
|
}
|
|
// Possible change in enumeration data, flush lists.
|
|
if (lRet == ERROR_SUCCESS)
|
|
{
|
|
key->FlushEnumList();
|
|
}
|
|
}
|
|
// No key, fall through to original API
|
|
else
|
|
{
|
|
lRet = ORIGINAL_API(RegSetValueExW)(
|
|
hKey,
|
|
lpValueName,
|
|
0,
|
|
dwType,
|
|
lpData,
|
|
cbData);
|
|
}
|
|
|
|
DPFN( ELEVEL(lRet), "%08lx=SetValueW(Key=%04lx)",
|
|
lRet,
|
|
hKey);
|
|
|
|
if (key)
|
|
{
|
|
DPFN( ELEVEL(lRet), " Path=%S\\%S", key->wzPath, lpValueName);
|
|
}
|
|
else
|
|
{
|
|
DPFN( ELEVEL(lRet), " Value=%S", lpValueName);
|
|
}
|
|
|
|
if (SUCCESS(lRet) &&
|
|
((dwType == REG_SZ) ||
|
|
(dwType == REG_EXPAND_SZ)))
|
|
{
|
|
DPFN( eDbgLevelInfo, " Result=%S", lpData);
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
DPFN( eDbgLevelError, "Exception occurred in SetValueW");
|
|
lRet = ERROR_BAD_ARGUMENTS;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegDeleteKeyA.
|
|
|
|
Algorithm:
|
|
1. Convert key name to Unicode.
|
|
2. Call DeleteKeyW
|
|
|
|
Arguments:
|
|
|
|
hKey - Key that contains subkey to delete.
|
|
lpSubKey - Key name to delete.
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS on success, failure code otherwise.
|
|
|
|
History:
|
|
|
|
08/07/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::DeleteKeyA(
|
|
IN HKEY hKey,
|
|
IN LPCSTR lpSubKey
|
|
)
|
|
{
|
|
LONG lRet;
|
|
DWORD dwSize;
|
|
WCHAR* wszSubKey = NULL;
|
|
|
|
dwSize = (DWORD)(lstrlenA(lpSubKey) + 1);
|
|
dwSize *= sizeof(WCHAR);
|
|
wszSubKey = (WCHAR*)malloc(dwSize);
|
|
if (wszSubKey == NULL)
|
|
{
|
|
DPFN( eDbgLevelError, szOutOfMemory);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
if (MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
lpSubKey,
|
|
-1,
|
|
(LPWSTR)wszSubKey,
|
|
dwSize/sizeof(WCHAR)) == 0)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
lRet = DeleteKeyW(hKey, wszSubKey);
|
|
|
|
free(wszSubKey);
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for DeleteKeyW.
|
|
|
|
Algorithm:
|
|
1. If key is not protected, delete key.
|
|
2. If in 9x compat mode, recursively delete all subkeys.
|
|
|
|
Arguments:
|
|
|
|
hKey - Key to that contains subkey to delete.
|
|
lpSubKey - Name of key to delete
|
|
|
|
Return Value:
|
|
|
|
ERROR_SUCCESS on success, failure code otherwise.
|
|
|
|
History:
|
|
|
|
08/07/2001 mikrause Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::DeleteKeyW(
|
|
IN HKEY hKey,
|
|
IN LPCWSTR lpSubKey
|
|
)
|
|
{
|
|
LONG hRet;
|
|
OPENKEY* key = FindOpenKey(hKey);
|
|
LPWSTR wzPath = NULL;
|
|
BOOL bProtected;
|
|
|
|
// Key not found, assume it's a root key.
|
|
if (!key)
|
|
{
|
|
DPFN( eDbgLevelInfo, "Key not found!");
|
|
wzPath = MakePath(hKey, 0, lpSubKey);
|
|
if (!wzPath)
|
|
{
|
|
DPFN(eDbgLevelError, szOutOfMemory);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
DPFN( eDbgLevelInfo, "Using path %S", wzPath);
|
|
}
|
|
else if (lpSubKey)
|
|
{
|
|
DWORD dwSize = wcslen(key->wzPath) + wcslen(L"\\") + wcslen(lpSubKey) + 1;
|
|
wzPath = (LPWSTR) malloc(dwSize * sizeof(WCHAR));
|
|
if (!wzPath)
|
|
{
|
|
DPFN(eDbgLevelError, szOutOfMemory);
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
ZeroMemory(wzPath, dwSize);
|
|
|
|
StringCchCopyW(wzPath, dwSize, key->wzPath);
|
|
StringCchCatW(wzPath, dwSize, L"\\");
|
|
StringCchCatW(wzPath, dwSize, lpSubKey);
|
|
}
|
|
|
|
bProtected = (key && CheckProtected(key->wzPath))
|
|
|| (wzPath && CheckProtected(wzPath));
|
|
if (!bProtected)
|
|
{
|
|
if (g_bWin9x)
|
|
{
|
|
//
|
|
// Find out whether hKey has any subkeys under it or not.
|
|
// If not, then proceed as normal.
|
|
// If yes, recursively delete the subkeys under it
|
|
// Then proceed as normal.
|
|
//
|
|
|
|
DWORD cSize = 0;
|
|
WCHAR lpSubKeyName[MAX_PATH];
|
|
HKEY hSubKey;
|
|
|
|
DPFN( eDbgLevelInfo, "RegDeleteKeyW called with hKey: %x, SubKey: %S", hKey, lpSubKey);
|
|
|
|
hRet = ORIGINAL_API(RegOpenKeyExW)(
|
|
hKey,
|
|
lpSubKey,
|
|
0,
|
|
KEY_ENUMERATE_SUB_KEYS,
|
|
&hSubKey);
|
|
|
|
if (SUCCESS(hRet))
|
|
{
|
|
for (;;)
|
|
{
|
|
cSize = MAX_PATH;
|
|
|
|
hRet = ORIGINAL_API(RegEnumKeyExW)(
|
|
hSubKey,
|
|
0,
|
|
lpSubKeyName,
|
|
&cSize,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
);
|
|
|
|
if (SUCCESS(hRet))
|
|
{
|
|
LOGN( eDbgLevelInfo,
|
|
"[DeleteKeyW] Deleting subkey %S for key %S.",
|
|
lpSubKeyName,
|
|
lpSubKey);
|
|
|
|
hRet = DeleteKeyW(
|
|
hSubKey,
|
|
lpSubKeyName);
|
|
|
|
if (SUCCESS(hRet))
|
|
{
|
|
LOGN( eDbgLevelInfo, "[DeleteKeyW] subkey %S was deleted.",lpSubKeyName);
|
|
}
|
|
else
|
|
{
|
|
LOGN( eDbgLevelInfo, "[DeleteKeyW] subkey %S was not deleted.",lpSubKeyName);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DPFN( eDbgLevelInfo, "[DeleteKeyW] No more subkey under key %S.",lpSubKey);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ORIGINAL_API(RegCloseKey)(hSubKey);
|
|
}
|
|
}
|
|
|
|
DPFN( eDbgLevelInfo, "[RegDeleteKeyW] Deleting subkey %S.",lpSubKey);
|
|
|
|
hRet = ORIGINAL_API(RegDeleteKeyW)(
|
|
hKey,
|
|
lpSubKey);
|
|
}
|
|
else
|
|
{
|
|
// Protected, just say it succeeded
|
|
hRet = ERROR_SUCCESS;
|
|
}
|
|
|
|
if (wzPath)
|
|
{
|
|
free(wzPath);
|
|
}
|
|
|
|
// Possible change in enumeration data, flush lists.
|
|
FlushEnumLists();
|
|
|
|
return hRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Function Description:
|
|
|
|
Wrapper for RegCloseKey. Note that we make sure we know about the key before closing it.
|
|
|
|
Algorithm:
|
|
1. Run the list of open keys and free if found
|
|
2. Close the key
|
|
|
|
Arguments:
|
|
|
|
IN hKey - Handle to open key to close
|
|
|
|
Return Value:
|
|
|
|
Error code or ERROR_SUCCESS
|
|
|
|
History:
|
|
|
|
01/06/2000 linstev Created
|
|
|
|
--*/
|
|
|
|
LONG
|
|
CVirtualRegistry::CloseKey(
|
|
IN HKEY hKey
|
|
)
|
|
{
|
|
OPENKEY *key = OpenKeys, *last = NULL;
|
|
LONG lRet;
|
|
|
|
__try
|
|
{
|
|
lRet = ERROR_INVALID_HANDLE;
|
|
|
|
while (key)
|
|
{
|
|
if (key->hkOpen == hKey)
|
|
{
|
|
if (last)
|
|
{
|
|
last->next = key->next;
|
|
}
|
|
else
|
|
{
|
|
OpenKeys = key->next;
|
|
}
|
|
|
|
lRet = ORIGINAL_API(RegCloseKey)(hKey);
|
|
|
|
free(key->wzPath);
|
|
free(key);
|
|
break;
|
|
}
|
|
|
|
last = key;
|
|
key = key->next;
|
|
}
|
|
|
|
if (key == NULL)
|
|
{
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
DPFN( ELEVEL(lRet), "%08lx=CloseKey(Key=%04lx)", lRet, hKey);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
lRet = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegCreateKeyA)(
|
|
HKEY hKey,
|
|
LPCSTR lpSubKey,
|
|
PHKEY phkResult
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.OpenKeyA(
|
|
hKey,
|
|
lpSubKey,
|
|
0,
|
|
REG_OPTION_NON_VOLATILE,
|
|
MAXIMUM_ALLOWED,
|
|
NULL,
|
|
phkResult,
|
|
0,
|
|
TRUE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegCreateKeyW)(
|
|
HKEY hKey,
|
|
LPCWSTR lpSubKey,
|
|
PHKEY phkResult
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.OpenKeyW(
|
|
hKey,
|
|
lpSubKey,
|
|
0,
|
|
REG_OPTION_NON_VOLATILE,
|
|
MAXIMUM_ALLOWED,
|
|
NULL,
|
|
phkResult,
|
|
0,
|
|
TRUE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegCreateKeyExA)(
|
|
HKEY hKey,
|
|
LPCSTR lpSubKey,
|
|
DWORD /* Reserved */,
|
|
LPSTR lpClass,
|
|
DWORD dwOptions,
|
|
REGSAM samDesired,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
|
PHKEY phkResult,
|
|
LPDWORD lpdwDisposition
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.OpenKeyA(
|
|
hKey,
|
|
lpSubKey,
|
|
lpClass,
|
|
dwOptions,
|
|
samDesired,
|
|
lpSecurityAttributes,
|
|
phkResult,
|
|
lpdwDisposition,
|
|
TRUE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegCreateKeyExW)(
|
|
HKEY hKey,
|
|
LPCWSTR lpSubKey,
|
|
DWORD /* Reserved */,
|
|
LPWSTR lpClass,
|
|
DWORD dwOptions,
|
|
REGSAM samDesired,
|
|
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
|
|
PHKEY phkResult,
|
|
LPDWORD lpdwDisposition
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.OpenKeyW(
|
|
hKey,
|
|
lpSubKey,
|
|
lpClass,
|
|
dwOptions,
|
|
samDesired,
|
|
lpSecurityAttributes,
|
|
phkResult,
|
|
lpdwDisposition,
|
|
TRUE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegOpenKeyA)(
|
|
HKEY hKey,
|
|
LPCSTR lpSubKey,
|
|
PHKEY phkResult
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.OpenKeyA(hKey, lpSubKey, 0, 0, MAXIMUM_ALLOWED, NULL, phkResult, 0, FALSE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegOpenKeyW)(
|
|
HKEY hKey,
|
|
LPCWSTR lpSubKey,
|
|
PHKEY phkResult
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.OpenKeyW(hKey, lpSubKey, 0, 0, MAXIMUM_ALLOWED, NULL, phkResult, 0, FALSE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
|
|
LONG
|
|
APIHOOK(RegOpenKeyExA)(
|
|
HKEY hKey,
|
|
LPCSTR lpSubKey,
|
|
DWORD /* ulOptions */,
|
|
REGSAM samDesired,
|
|
PHKEY phkResult
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.OpenKeyA(hKey, lpSubKey, 0, 0, samDesired, NULL, phkResult, 0, FALSE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegOpenKeyExW)(
|
|
HKEY hKey,
|
|
LPCWSTR lpSubKey,
|
|
DWORD /* ulOptions */,
|
|
REGSAM samDesired,
|
|
PHKEY phkResult
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.OpenKeyW(hKey, lpSubKey, 0, 0, samDesired, NULL, phkResult, 0, FALSE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Not yet implemented
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegQueryValueA)(
|
|
HKEY hKey,
|
|
LPCSTR lpSubKey,
|
|
LPSTR lpData,
|
|
PLONG lpcbData
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return ORIGINAL_API(RegQueryValueA)(
|
|
hKey,
|
|
lpSubKey,
|
|
lpData,
|
|
lpcbData);
|
|
}
|
|
|
|
/*++
|
|
|
|
Not yet implemented
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegQueryValueW)(
|
|
HKEY hKey,
|
|
LPCWSTR lpSubKey,
|
|
LPWSTR lpData,
|
|
PLONG lpcbData
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return ORIGINAL_API(RegQueryValueW)(
|
|
hKey,
|
|
lpSubKey,
|
|
lpData,
|
|
lpcbData);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegQueryValueExA)(
|
|
HKEY hKey,
|
|
LPSTR lpValueName,
|
|
LPDWORD /* lpReserved */,
|
|
LPDWORD lpType,
|
|
LPBYTE lpData,
|
|
LPDWORD lpcbData
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.QueryValueA(hKey, lpValueName, lpType, lpData, lpcbData);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegQueryValueExW)(
|
|
HKEY hKey,
|
|
LPWSTR lpValueName,
|
|
LPDWORD /* lpReserved */,
|
|
LPDWORD lpType,
|
|
LPBYTE lpData,
|
|
LPDWORD lpcbData
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.QueryValueW(hKey, lpValueName, lpType, lpData, lpcbData);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegCloseKey)(HKEY hKey)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.CloseKey(hKey);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegEnumValueA)(
|
|
HKEY hKey,
|
|
DWORD dwIndex,
|
|
LPSTR lpValueName,
|
|
LPDWORD lpcbValueName,
|
|
LPDWORD /* lpReserved */,
|
|
LPDWORD lpType,
|
|
LPBYTE lpData,
|
|
LPDWORD lpcbData
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.EnumValueA(
|
|
hKey,
|
|
dwIndex,
|
|
lpValueName,
|
|
lpcbValueName,
|
|
lpType,
|
|
lpData,
|
|
lpcbData);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegEnumValueW)(
|
|
HKEY hKey,
|
|
DWORD dwIndex,
|
|
LPWSTR lpValueName,
|
|
LPDWORD lpcbValueName,
|
|
LPDWORD /* lpReserved */,
|
|
LPDWORD lpType,
|
|
LPBYTE lpData,
|
|
LPDWORD lpcbData
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.EnumValueW(
|
|
hKey,
|
|
dwIndex,
|
|
lpValueName,
|
|
lpcbValueName,
|
|
lpType,
|
|
lpData,
|
|
lpcbData);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegEnumKeyExA)(
|
|
HKEY hKey,
|
|
DWORD dwIndex,
|
|
LPSTR lpName,
|
|
LPDWORD lpcbName,
|
|
LPDWORD /* lpReserved */,
|
|
LPSTR /* lpClass */,
|
|
LPDWORD /* lpcbClass */,
|
|
PFILETIME /* lpftLastWriteTime */
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.EnumKeyA(hKey, dwIndex, lpName, lpcbName);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegEnumKeyExW)(
|
|
HKEY hKey,
|
|
DWORD dwIndex,
|
|
LPWSTR lpName,
|
|
LPDWORD lpcbName,
|
|
LPDWORD /* lpReserved */,
|
|
LPWSTR /* lpClass */,
|
|
LPDWORD /* lpcbClass */,
|
|
PFILETIME /* lpftLastWriteTime */
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.EnumKeyW(hKey, dwIndex, lpName, lpcbName);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegEnumKeyA)(
|
|
HKEY hKey,
|
|
DWORD dwIndex,
|
|
LPSTR lpName,
|
|
DWORD cbName
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.EnumKeyA(hKey, dwIndex, lpName, &cbName);
|
|
}
|
|
|
|
/*++
|
|
|
|
Calls down to RegEnumKeyExW
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegEnumKeyW)(
|
|
HKEY hKey,
|
|
DWORD dwIndex,
|
|
LPWSTR lpName,
|
|
DWORD cbName
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.EnumKeyW(hKey, dwIndex, lpName, &cbName);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegQueryInfoKeyW)(
|
|
HKEY hKey,
|
|
LPWSTR lpClass,
|
|
LPDWORD lpcbClass,
|
|
LPDWORD lpReserved,
|
|
LPDWORD lpcSubKeys,
|
|
LPDWORD lpcbMaxSubKeyLen,
|
|
LPDWORD lpcbMaxClassLen,
|
|
LPDWORD lpcValues,
|
|
LPDWORD lpcbMaxValueNameLen,
|
|
LPDWORD lpcbMaxValueLen,
|
|
LPDWORD lpcbSecurityDescriptor,
|
|
PFILETIME lpftLastWriteTime
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.QueryInfoW(
|
|
hKey,
|
|
lpClass,
|
|
lpcbClass,
|
|
lpReserved,
|
|
lpcSubKeys,
|
|
lpcbMaxSubKeyLen,
|
|
lpcbMaxClassLen,
|
|
lpcValues,
|
|
lpcbMaxValueNameLen,
|
|
lpcbMaxValueLen,
|
|
lpcbSecurityDescriptor,
|
|
lpftLastWriteTime);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegQueryInfoKeyA)(
|
|
HKEY hKey,
|
|
LPSTR lpClass,
|
|
LPDWORD lpcbClass,
|
|
LPDWORD lpReserved,
|
|
LPDWORD lpcSubKeys,
|
|
LPDWORD lpcbMaxSubKeyLen,
|
|
LPDWORD lpcbMaxClassLen,
|
|
LPDWORD lpcValues,
|
|
LPDWORD lpcbMaxValueNameLen,
|
|
LPDWORD lpcbMaxValueLen,
|
|
LPDWORD lpcbSecurityDescriptor,
|
|
PFILETIME lpftLastWriteTime
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.QueryInfoA(
|
|
hKey,
|
|
lpClass,
|
|
lpcbClass,
|
|
lpReserved,
|
|
lpcSubKeys,
|
|
lpcbMaxSubKeyLen,
|
|
lpcbMaxClassLen,
|
|
lpcValues,
|
|
lpcbMaxValueNameLen,
|
|
lpcbMaxValueLen,
|
|
lpcbSecurityDescriptor,
|
|
lpftLastWriteTime);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegSetValueExA)(
|
|
HKEY hKey,
|
|
LPCSTR lpSubKey,
|
|
DWORD /* Reserved */,
|
|
DWORD dwType,
|
|
CONST BYTE * lpData,
|
|
DWORD cbData
|
|
)
|
|
{
|
|
LONG lRet = 0;
|
|
|
|
if (!lpData && cbData)
|
|
{
|
|
lRet = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
CRegLock lock;
|
|
lRet = VRegistry.SetValueA(hKey, lpSubKey, dwType, lpData, cbData);
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegSetValueExW)(
|
|
HKEY hKey,
|
|
LPCWSTR lpSubKey,
|
|
DWORD /* Reserved */,
|
|
DWORD dwType,
|
|
CONST BYTE * lpData,
|
|
DWORD cbData
|
|
)
|
|
{
|
|
LONG lRet = 0;
|
|
|
|
if (!lpData && cbData)
|
|
{
|
|
lRet = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
CRegLock lock;
|
|
lRet = VRegistry.SetValueW(hKey, lpSubKey, dwType, lpData, cbData);
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegDeleteKeyA)(
|
|
HKEY hKey,
|
|
LPCSTR lpSubKey
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.DeleteKeyA(hKey, lpSubKey);
|
|
}
|
|
|
|
/*++
|
|
|
|
Pass through to virtual registry to handle call.
|
|
|
|
--*/
|
|
|
|
LONG
|
|
APIHOOK(RegDeleteKeyW)(
|
|
HKEY hKey,
|
|
LPCWSTR lpSubKey
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.DeleteKeyW(hKey, lpSubKey);
|
|
}
|
|
|
|
LONG
|
|
APIHOOK(RegConnectRegistryW)(
|
|
LPCWSTR lpMachineName,
|
|
HKEY hKey,
|
|
PHKEY phkResult
|
|
)
|
|
{
|
|
CRegLock Lock;
|
|
|
|
return VRegistry.OpenKeyW(
|
|
hKey,
|
|
NULL,
|
|
0,
|
|
0,
|
|
MAXIMUM_ALLOWED,
|
|
NULL,
|
|
phkResult,
|
|
0,
|
|
FALSE,
|
|
TRUE,
|
|
lpMachineName);
|
|
}
|
|
|
|
LONG
|
|
APIHOOK(RegConnectRegistryA)(
|
|
LPCSTR lpMachineName,
|
|
HKEY hKey,
|
|
PHKEY phkResult
|
|
)
|
|
{
|
|
WCHAR wMachineName[MAX_COMPUTERNAME_LENGTH + 1] = L"";
|
|
|
|
if (lpMachineName)
|
|
{
|
|
if (MultiByteToWideChar(
|
|
CP_ACP,
|
|
0,
|
|
lpMachineName,
|
|
-1,
|
|
wMachineName,
|
|
MAX_COMPUTERNAME_LENGTH + 1) == 0)
|
|
{
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
return APIHOOK(RegConnectRegistryW)(wMachineName, hKey, phkResult);
|
|
}
|
|
|
|
/*++
|
|
|
|
Parse the command line for fixes:
|
|
|
|
FIXA(param); FIXB(param); FIXC(param) ...
|
|
|
|
param is optional, and can be omitted (along with parenthesis's)
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
ParseCommandLineA(
|
|
LPCSTR lpCommandLine
|
|
)
|
|
{
|
|
const char szDefault[] = "Win9x";
|
|
|
|
// Add all the defaults if no command line is specified
|
|
if (!lpCommandLine || (lpCommandLine[0] == '\0'))
|
|
{
|
|
// Default to win9x API emulation
|
|
g_bWin9x = TRUE;
|
|
lpCommandLine = szDefault;
|
|
}
|
|
|
|
CSTRING_TRY
|
|
{
|
|
CStringToken csCommandLine(lpCommandLine, " ,\t;");
|
|
CString csTok;
|
|
int nLeftParam, nRightParam;
|
|
CString csParam;
|
|
|
|
|
|
VENTRY *ventry;
|
|
|
|
//
|
|
// Run the string, looking for fix names
|
|
//
|
|
|
|
DPFN( eDbgLevelInfo, "----------------------------------");
|
|
DPFN( eDbgLevelInfo, " Virtual registry ");
|
|
DPFN( eDbgLevelInfo, "----------------------------------");
|
|
DPFN( eDbgLevelInfo, "Adding command line:");
|
|
|
|
while (csCommandLine.GetToken(csTok))
|
|
{
|
|
PURPOSE ePurpose;
|
|
|
|
// Get the parameter
|
|
nLeftParam = csTok.Find(L'(');
|
|
nRightParam = csTok.Find(L')');
|
|
if (nLeftParam != -1 &&
|
|
nRightParam != -1)
|
|
{
|
|
if ( (nLeftParam + 1) < (nRightParam - 1))
|
|
{
|
|
csParam = csTok.Mid(nLeftParam+1, nRightParam-nLeftParam-1);
|
|
}
|
|
|
|
// Strip off the () from the token.
|
|
csTok.Truncate(nLeftParam);
|
|
}
|
|
else
|
|
{
|
|
csParam = L"";
|
|
}
|
|
|
|
if (csTok.CompareNoCase(L"Win9x") == 0)
|
|
{
|
|
// Turn on all win9x fixes
|
|
ePurpose = eWin9x;
|
|
g_bWin9x = TRUE;
|
|
}
|
|
else if (csTok.CompareNoCase(L"WinNT") == 0)
|
|
{
|
|
// Turn on all NT fixes
|
|
ePurpose = eWinNT;
|
|
g_bWin9x = FALSE;
|
|
}
|
|
else if (csTok.CompareNoCase(L"Win2K") == 0)
|
|
{
|
|
// Turn on all Win2K fixes
|
|
ePurpose = eWin2K;
|
|
g_bWin9x = FALSE;
|
|
}
|
|
else if (csTok.CompareNoCase(L"WinXP") == 0)
|
|
{
|
|
// Turn on all Win2K fixes
|
|
ePurpose = eWinXP;
|
|
g_bWin9x = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// A custom fix
|
|
ePurpose = eCustom;
|
|
}
|
|
|
|
// Find the specified fix and run it's function
|
|
ventry = g_pVList;
|
|
while (ventry && (ventry->cName[0]))
|
|
{
|
|
if (((ePurpose != eCustom) && (ventry->ePurpose == ePurpose)) ||
|
|
((ePurpose == eCustom) && (csTok.CompareNoCase(ventry->cName) == 0)))
|
|
{
|
|
if (ventry->bShouldCall == FALSE)
|
|
{
|
|
ventry->szParam = (char*) malloc(csParam.GetLength() + 1);
|
|
if (ventry->szParam)
|
|
{
|
|
if (SUCCEEDED(StringCchCopyA(ventry->szParam, csParam.GetLength() + 1, csParam.GetAnsi())))
|
|
{
|
|
ventry->bShouldCall = TRUE;
|
|
}
|
|
else
|
|
{
|
|
free(ventry->szParam);
|
|
ventry->szParam = NULL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
ventry++;
|
|
}
|
|
}
|
|
|
|
DPFN( eDbgLevelInfo, "----------------------------------");
|
|
}
|
|
CSTRING_CATCH
|
|
{
|
|
DPFN(eDbgLevelError, szOutOfMemory);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*++
|
|
|
|
Initialize all the registry hooks
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
NOTIFY_FUNCTION(
|
|
DWORD fdwReason
|
|
)
|
|
{
|
|
if (fdwReason == DLL_PROCESS_ATTACH)
|
|
{
|
|
if (InitializeCriticalSectionAndSpinCount(&csRegCriticalSection, 0x80000000) == FALSE ||
|
|
VRegistry.Init() == FALSE ||
|
|
ParseCommandLineA(COMMAND_LINE) == FALSE)
|
|
{
|
|
DPFN(eDbgLevelError, szOutOfMemory);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// Ignore cleanup because some apps call registry functions during process detach.
|
|
/*
|
|
if (fdwReason == DLL_PROCESS_DETACH)
|
|
{
|
|
if (g_bInitialized)
|
|
{
|
|
VRegistry.Free();
|
|
|
|
DeleteCriticalSection(&csRegCriticalSection);
|
|
}
|
|
|
|
DeleteCriticalSection(&csRegTestCriticalSection);
|
|
|
|
return;
|
|
}
|
|
*/
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
HOOK_BEGIN
|
|
|
|
CALL_NOTIFY_FUNCTION
|
|
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegConnectRegistryA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegConnectRegistryW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyExA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyExW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegQueryValueExA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegQueryValueExW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegCloseKey);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegQueryValueA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegQueryValueW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyExA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyExW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegEnumValueA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegEnumValueW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegEnumKeyA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegEnumKeyW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegEnumKeyExA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegEnumKeyExW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegQueryInfoKeyA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegQueryInfoKeyW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegSetValueExA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegSetValueExW);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegDeleteKeyA);
|
|
APIHOOK_ENTRY(ADVAPI32.DLL, RegDeleteKeyW);
|
|
|
|
HOOK_END
|
|
|
|
IMPLEMENT_SHIM_END
|