Windows2003-3790/windows/appcompat/shims/layer/vregistry.cpp
2020-09-30 16:53:55 +02:00

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