Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3311 lines
84 KiB

/*++
Copyright (c) Microsoft Corporation. All rights reserved.
Module Name:
LogRegistryChanges.cpp
Abstract:
This AppVerifier shim hooks all the registry APIs
that change the state of the system and logs their
associated data to a text file.
Notes:
This is a general purpose shim.
History:
08/17/2001 rparsons Created
09/20/2001 rparsons File I/O operations use NT APIs
09/23/2001 rparsons VLOG with log file location
10/06/2001 rparsons Open key handles are never removed from the list
02/20/2002 rparsons Implemented strsafe functions
--*/
#include "precomp.h"
#include "rtlutils.h"
IMPLEMENT_SHIM_BEGIN(LogRegistryChanges)
#include "ShimHookMacro.h"
#include "ShimCString.h"
#include "LogRegistryChanges.h"
BEGIN_DEFINE_VERIFIER_LOG(LogRegistryChanges)
VERIFIER_LOG_ENTRY(VLOG_LOGREGCHANGES_LOGLOC)
END_DEFINE_VERIFIER_LOG(LogRegistryChanges)
INIT_VERIFIER_LOG(LogRegistryChanges);
//
// Stores the NT path to the file system log file for the current session.
//
UNICODE_STRING g_strLogFilePath;
//
// Stores the DOS path to the file system log file for the current session.
//
WCHAR g_wszLogFilePath[MAX_PATH];
//
// Head of our open key handle linked list.
//
LIST_ENTRY g_OpenKeyListHead;
//
// Temporary buffer stored on the heap.
// Used when creating XML elements to log.
// This doesn't get freed.
//
LPWSTR g_pwszTempBuffer;
//
// Size of the temporary buffer above.
//
DWORD g_cbTempBufferSize;
//
// Stores the unique id used for NULL value names.
//
WCHAR g_wszUniqueId[MAX_PATH * 2];
//
// Temporary buffers stored on the heap.
// Used when extracting old & new data for logging.
// These don't get freed.
//
LPWSTR g_pwszOriginalData;
LPWSTR g_pwszFinalData;
//
// Size of the temporary buffers above.
//
DWORD g_cbOriginalDataBufferSize;
DWORD g_cbFinalDataBufferSize;
//
// Critical section that keeps us safe while using linked-lists, etc.
//
CCriticalSection g_csCritSec;
/*++
Writes an entry to the log file.
--*/
void
WriteEntryToLog(
IN LPCWSTR pwszEntry
)
{
int cbSize;
HANDLE hFile;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
LARGE_INTEGER liOffset;
NTSTATUS status;
//
// Note that we have to use native APIs throughout this function
// to avoid a problem with circular hooking. That is, if we simply
// call WriteFile, which is exported from kernel32, it will call NtWriteFile,
// which is a call that we hook, in turn leaving us in and endless loop.
//
//
// Attempt to get a handle to our log file.
//
InitializeObjectAttributes(&ObjectAttributes,
&g_strLogFilePath,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtCreateFile(&hFile,
FILE_APPEND_DATA | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
0,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(status)) {
DPFN(eDbgLevelError,
"[WriteEntryToLog] 0x%X Failed to open log",
status);
return;
}
//
// Write the data out to the file.
//
cbSize = wcslen(pwszEntry);
cbSize *= sizeof(WCHAR);
IoStatusBlock.Status = 0;
IoStatusBlock.Information = 0;
liOffset.LowPart = 0;
liOffset.HighPart = 0;
status = NtWriteFile(hFile,
NULL,
NULL,
NULL,
&IoStatusBlock,
(PVOID)pwszEntry,
(ULONG)cbSize,
&liOffset,
NULL);
if (!NT_SUCCESS(status)) {
DPFN(eDbgLevelError,
"[WriteEntryToLog] 0x%X Failed to make entry",
status);
goto exit;
}
exit:
NtClose(hFile);
}
/*++
Creates our XML file to store our results in.
--*/
BOOL
InitializeLogFile(
void
)
{
BOOL bReturn = FALSE;
BOOL bStatus = FALSE;
HANDLE hFile;
DWORD cchSize;
WCHAR* pwchSlash = NULL;
WCHAR* pwchDot = NULL;
WCHAR wszLogFilePath[MAX_PATH];
WCHAR wszModPathName[MAX_PATH];
WCHAR wszLogFile[MAX_PATH / 2];
WCHAR wszShortName[MAX_PATH / 2];
WCHAR wszLogHdr[512];
WCHAR wchUnicodeHdr = 0xFEFF;
HRESULT hr;
NTSTATUS status;
SYSTEMTIME st;
UNICODE_STRING strLogFile;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
//
// Format the log header.
//
cchSize = GetModuleFileName(NULL, wszModPathName, ARRAYSIZE(wszModPathName));
if (cchSize > ARRAYSIZE(wszModPathName) || cchSize == 0) {
StringCchCopy(wszModPathName, ARRAYSIZE(wszModPathName), L"unknown");
}
hr = StringCchPrintf(wszLogHdr,
ARRAYSIZE(wszLogHdr),
L"%lc<?xml version=\"1.0\" encoding=\"UTF-16\"?>\r\n<APPLICATION NAME=\"%ls\">\r\n",
wchUnicodeHdr,
wszModPathName);
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[InitializeLogFile] 0x%08X Failed to format log header",
HRESULT_CODE(hr));
return FALSE;
}
//
// Get the path where log files are stored.
//
cchSize = GetAppVerifierLogPath(wszLogFilePath, ARRAYSIZE(wszLogFilePath));
if (cchSize > ARRAYSIZE(wszLogFilePath) || cchSize == 0) {
DPFN(eDbgLevelError, "[InitializeLogFile] Failed to get log path");
return FALSE;
}
//
// See if the directory exists - but don't try to create it.
//
if (GetFileAttributes(wszLogFilePath) == -1) {
DPFN(eDbgLevelError,
"[InitializeLogFile] Log file directory '%ls' does not exist",
wszLogFilePath);
return FALSE;
}
//
// Set up the log filename.
// The format is this: processname_registry_yyyymmdd_hhmmss.xml
//
GetLocalTime(&st);
*wszShortName = 0;
pwchDot = wcsrchr(wszModPathName, '.');
if (pwchDot) {
*pwchDot = 0;
}
pwchSlash = wcsrchr(wszModPathName, '\\');
if (pwchSlash) {
StringCchCopy(wszShortName, ARRAYSIZE(wszShortName), ++pwchSlash);
}
hr = StringCchPrintf(wszLogFile,
ARRAYSIZE(wszLogFile),
L"%ls_registry_%02hu%02hu%02hu_%02hu%02hu%02hu.xml",
wszShortName,
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond);
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[InitializeLogFile] 0x%08X Failed to format log filename",
HRESULT_CODE(hr));
return FALSE;
}
//
// See if the file already exists.
//
SetCurrentDirectory(wszLogFilePath);
if (GetFileAttributes(wszLogFile) != -1) {
//
// Reformat the filename.
//
hr = StringCchPrintf(wszLogFile,
ARRAYSIZE(wszLogFile),
L"%ls_registry_%02hu%02hu%02hu_%02hu%02hu%02hu_%lu.xml",
wszShortName,
st.wYear,
st.wMonth,
st.wDay,
st.wHour,
st.wMinute,
st.wSecond,
GetTickCount());
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[InitializeLogFile] 0x%08X Failed to reformat log filename",
HRESULT_CODE(hr));
return FALSE;
}
}
StringCchCat(wszLogFilePath, ARRAYSIZE(wszLogFilePath), L"\\");
hr = StringCchCat(wszLogFilePath, ARRAYSIZE(wszLogFilePath), wszLogFile);
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[InitializeLogFile] 0x%08X Failed to format path to log",
HRESULT_CODE(hr));
return FALSE;
}
//
// Preserve this path for later use.
//
hr = StringCchCopy(g_wszLogFilePath,
ARRAYSIZE(g_wszLogFilePath),
wszLogFilePath);
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[InitializeLogFile] 0x%08X Failed to save path to log",
HRESULT_CODE(hr));
return FALSE;
}
bStatus = RtlDosPathNameToNtPathName_U(wszLogFilePath,
&strLogFile,
NULL,
NULL);
if (!bStatus) {
DPFN(eDbgLevelError, "[InitializeLogFile] DOS path --> NT path failed");
return FALSE;
}
//
// Create the log file.
//
InitializeObjectAttributes(&ObjectAttributes,
&strLogFile,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
status = NtCreateFile(&hFile,
GENERIC_WRITE | SYNCHRONIZE,
&ObjectAttributes,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
0,
FILE_CREATE,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if (!NT_SUCCESS(status)) {
DPFN(eDbgLevelError,
"[InitializeLogFile] 0x%X Failed to create log",
status);
goto cleanup;
}
NtClose(hFile);
//
// Preserve the NT path for later use.
//
status = ShimDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE |
RTL_DUPLICATE_UNICODE_STRING_ALLOCATE_NULL_STRING,
&strLogFile,
&g_strLogFilePath);
if (!NT_SUCCESS(status)) {
DPFN(eDbgLevelError,
"[InitializeLogFile] 0x%X Failed to save log file path",
status);
goto cleanup;
}
//
// Write the header to the log.
//
WriteEntryToLog(wszLogHdr);
bReturn = TRUE;
cleanup:
if (bStatus) {
RtlFreeUnicodeString(&strLogFile);
}
return bReturn;
}
/*++
Writes the closing element to the file and outputs the log file location.
--*/
BOOL
CloseLogFile(
void
)
{
WCHAR wszBuffer[] = L"</APPLICATION>";
WriteEntryToLog(wszBuffer);
VLOG(VLOG_LEVEL_INFO, VLOG_LOGREGCHANGES_LOGLOC, "%ls", g_wszLogFilePath);
return TRUE;
}
/*++
Converts from ANSI to Unicode. The caller must free the buffer.
--*/
BOOL
ConvertAnsiToUnicode(
IN LPCSTR pszAnsiString,
OUT LPWSTR* pwszUnicodeString
)
{
int cchSize = 0;
cchSize = lstrlenA(pszAnsiString);
if (cchSize) {
*pwszUnicodeString = (LPWSTR)MemAlloc(++cchSize * sizeof(WCHAR));
if (!*pwszUnicodeString) {
DPFN(eDbgLevelError,
"[ConvertAnsiToUnicode] Failed to allocate memory");
return FALSE;
}
cchSize = MultiByteToWideChar(CP_ACP,
0,
pszAnsiString,
-1,
*pwszUnicodeString,
cchSize);
if (cchSize == 0) {
DPFN(eDbgLevelError,
"[ConvertAnsiToUnicode] 0x%08X Ansi -> Unicode failed",
GetLastError());
MemFree(*pwszUnicodeString);
return FALSE;
}
}
return TRUE;
}
/*++
Converts a list of NULL terminated strings from ANSI to Unicode.
The caller must free the buffer.
--*/
BOOL
ConvertMultiSzToUnicode(
IN LPCSTR pszAnsiStringList,
OUT LPWSTR* pwszWideStringList
)
{
int nLen = 0;
UINT cchSize = 0;
UINT cchWideSize = 0;
UINT cchTotalSize = 0;
LPCSTR pszAnsi = NULL;
LPWSTR pwszTemp = NULL;
if (!pszAnsiStringList) {
DPFN(eDbgLevelError, "[ConvertMultiSzToUnicode] Invalid parameter");
return FALSE;
}
pszAnsi = pszAnsiStringList;
//
// Determine how large of a buffer we need to allocate.
//
do {
cchSize = lstrlenA(pszAnsi) + 1;
cchTotalSize += cchSize;
pszAnsi += cchSize;
} while (cchSize != 1);
if (cchTotalSize != 0) {
pwszTemp = *pwszWideStringList = (LPWSTR)MemAlloc(cchTotalSize * sizeof(WCHAR));
if (!*pwszWideStringList) {
DPFN(eDbgLevelError,
"[ConvertMultiSzToUnicode] No memory for buffer");
return FALSE;
}
}
//
// Perform the ANSI to Unicode conversion.
//
pszAnsi = pszAnsiStringList;
do {
nLen = lstrlenA(pszAnsi) + 1;
cchWideSize = MultiByteToWideChar(
CP_ACP,
0,
pszAnsi,
-1,
pwszTemp,
nLen);
pszAnsi += nLen;
pwszTemp += cchWideSize;
} while (nLen != 1);
return TRUE;
}
/*++
Given a predefined key handle such as HKEY_LOCAL_MACHINE, return a string.
--*/
BOOL
PredefinedKeyToString(
IN HKEY hKey,
IN DWORD cchSize,
OUT LPWSTR* pwszString
)
{
if (hKey == HKEY_CLASSES_ROOT) {
StringCchCopy(*pwszString, cchSize, L"HKEY_CLASSES_ROOT");
}
else if (hKey == HKEY_CURRENT_CONFIG) {
StringCchCopy(*pwszString, cchSize, L"HKEY_CURRENT_CONFIG");
}
else if (hKey == HKEY_CURRENT_USER) {
StringCchCopy(*pwszString, cchSize, L"HKEY_CURRENT_USER");
}
else if (hKey == HKEY_LOCAL_MACHINE) {
StringCchCopy(*pwszString, cchSize, L"HKEY_LOCAL_MACHINE");
}
else if (hKey == HKEY_USERS) {
StringCchCopy(*pwszString, cchSize, L"HKEY_USERS");
}
else if (hKey == HKEY_DYN_DATA) {
StringCchCopy(*pwszString, cchSize, L"HKEY_DYN_DATA");
}
else if (hKey == HKEY_PERFORMANCE_DATA) {
StringCchCopy(*pwszString, cchSize, L"HKEY_PERFORMANCE_DATA");
}
else {
StringCchCopy(*pwszString, cchSize, L"Not recognized");
return FALSE;
}
return TRUE;
}
/*++
Displays the name associated with this object.
--*/
#if DBG
void
PrintNameFromKey(
IN HKEY hKey
)
{
NTSTATUS status;
WCHAR wszBuffer[MAX_PATH];
OBJECT_NAME_INFORMATION* poni = NULL;
*wszBuffer = 0;
poni = (OBJECT_NAME_INFORMATION*)wszBuffer;
status = NtQueryObject(hKey, ObjectNameInformation, poni, MAX_PATH, NULL);
if (NT_SUCCESS(status)) {
DPFN(eDbgLevelInfo,
"Key 0x%08X has name: %ls",
hKey,
poni->Name.Buffer);
}
}
#endif // DBG
/*++
Given a pointer to a key node, get the original data.
--*/
BOOL
CLogRegistry::GetOriginalDataForKey(
IN PLOG_OPEN_KEY pLogOpenKey,
IN PKEY_DATA pKeyData,
IN LPCWSTR pwszValueName
)
{
BOOL fReturn = FALSE;
HKEY hKeyLocal;
DWORD cbSize = 0, dwType = 0;
LONG lRetVal;
if (!pLogOpenKey || !pKeyData) {
DPFN(eDbgLevelError, "[GetOriginalDataForKey] Invalid parameter(s)");
return FALSE;
}
lRetVal = RegOpenKeyEx(pLogOpenKey->hKeyRoot,
pLogOpenKey->pwszSubKeyPath,
0,
KEY_QUERY_VALUE,
&hKeyLocal);
if (ERROR_SUCCESS != lRetVal) {
DPFN(eDbgLevelError, "[GetOriginalDataForKey] Failed to open key");
return FALSE;
}
//
// Query the size of the data. If the data doesn't exist, return success.
//
lRetVal = RegQueryValueEx(hKeyLocal,
pwszValueName,
0,
&dwType,
NULL,
&cbSize);
if (ERROR_SUCCESS != lRetVal) {
if (ERROR_FILE_NOT_FOUND == lRetVal) {
RegCloseKey(hKeyLocal);
return TRUE;
} else {
DPFN(eDbgLevelError, "[GetOldDataForKey] Failed to get data size");
goto cleanup;
}
}
//
// Update the flags to indicate that the value already exists.
//
pKeyData->dwFlags |= LRC_EXISTING_VALUE;
//
// Allocate a buffer large enough to store the old data.
//
if (dwType != REG_DWORD && dwType != REG_BINARY) {
pKeyData->pOriginalData = (PVOID)MemAlloc(cbSize * sizeof(WCHAR));
pKeyData->cbOriginalDataSize = cbSize * sizeof(WCHAR);
} else {
pKeyData->pOriginalData = (PVOID)MemAlloc(cbSize);
pKeyData->cbOriginalDataSize = cbSize;
}
if (!pKeyData->pOriginalData) {
DPFN(eDbgLevelError,
"[GetOriginalDataForKey] Failed to allocate memory");
goto cleanup;
}
pKeyData->dwOriginalValueType = dwType;
//
// Now make the call again this time getting the actual data.
//
lRetVal = RegQueryValueEx(hKeyLocal,
pwszValueName,
0,
0,
(LPBYTE)pKeyData->pOriginalData,
&cbSize);
if (ERROR_SUCCESS != lRetVal) {
DPFN(eDbgLevelError, "[GetOriginalDataForKey] Failed to get data");
goto cleanup;
}
fReturn = TRUE;
cleanup:
RegCloseKey(hKeyLocal);
return fReturn;
}
/*++
Given a pointer to a key node, get the final data.
--*/
BOOL
CLogRegistry::GetFinalDataForKey(
IN PLOG_OPEN_KEY pLogOpenKey,
IN PKEY_DATA pKeyData,
IN LPCWSTR pwszValueName
)
{
BOOL fReturn = FALSE;
HKEY hKeyLocal;
DWORD cbSize = 0, dwType = 0;
LONG lRetVal;
PVOID pTemp = NULL;
if (!pLogOpenKey || !pKeyData) {
DPFN(eDbgLevelError, "[GetFinalDataForKey] Invalid parameter(s)");
return FALSE;
}
lRetVal = RegOpenKeyEx(pLogOpenKey->hKeyRoot,
pLogOpenKey->pwszSubKeyPath,
0,
KEY_QUERY_VALUE,
&hKeyLocal);
if (ERROR_SUCCESS != lRetVal) {
DPFN(eDbgLevelError, "[GetFinalDataForKey] Failed to open key");
return FALSE;
}
//
// Query the size of the data. If the data doesn't exist, return success.
//
lRetVal = RegQueryValueEx(hKeyLocal,
pwszValueName,
0,
&dwType,
NULL,
&cbSize);
if (ERROR_SUCCESS != lRetVal) {
if (ERROR_FILE_NOT_FOUND == lRetVal) {
RegCloseKey(hKeyLocal);
return TRUE;
} else {
DPFN(eDbgLevelError,
"[GetFinalDataForKey] Failed to get data size");
goto cleanup;
}
}
//
// It's possible that multiple queries were issued against the same
// key. If this is the case, the buffer to hold the data has already
// been allocated. Determine if the block is large enough.
//
if (pKeyData->pFinalData) {
if (dwType != REG_DWORD && dwType != REG_BINARY) {
//
// If MemReAlloc fails, we would lose the pointer that
// we already had in pKeyData->pFinalData. This preserves
// the pointer.
//
if (pKeyData->cbFinalDataSize < (cbSize * sizeof(WCHAR))) {
pKeyData->cbFinalDataSize = cbSize * sizeof(WCHAR);
pTemp = MemReAlloc(pKeyData->pFinalData,
cbSize * sizeof(WCHAR));
}
} else {
if (pKeyData->cbFinalDataSize < cbSize) {
pKeyData->cbFinalDataSize = cbSize;
pTemp = MemReAlloc(pKeyData->pFinalData,
cbSize);
}
}
if (pTemp) {
pKeyData->pFinalData = pTemp;
} else {
DPFN(eDbgLevelError,
"[GetFinalDataForKey] Failed to reallocate memory");
goto cleanup;
}
} else {
if (dwType != REG_DWORD && dwType != REG_BINARY) {
pKeyData->pFinalData = MemAlloc(cbSize * sizeof(WCHAR));
pKeyData->cbFinalDataSize = cbSize * sizeof(WCHAR);
} else {
pKeyData->pFinalData = MemAlloc(cbSize);
pKeyData->cbFinalDataSize = cbSize;
}
}
if (!pKeyData->pFinalData) {
DPFN(eDbgLevelError, "[GetFinalDataForKey] Failed to allocate memory");
goto cleanup;
}
pKeyData->dwFinalValueType = dwType;
//
// Now make the call again this time getting the actual data.
//
lRetVal = RegQueryValueEx(hKeyLocal,
pwszValueName,
0,
0,
(LPBYTE)pKeyData->pFinalData,
&cbSize);
if (ERROR_SUCCESS != lRetVal) {
DPFN(eDbgLevelError, "[GetFinalDataForKey] Failed to get data");
goto cleanup;
}
fReturn = TRUE;
cleanup:
RegCloseKey(hKeyLocal);
return fReturn;
}
/*++
Given a value name, attempt to find it in the list.
This function may not always return a pointer.
--*/
PKEY_DATA
CLogRegistry::FindValueNameInList(
IN LPCWSTR pwszValueName,
IN PLOG_OPEN_KEY pOpenKey
)
{
BOOL fFound = FALSE;
PLIST_ENTRY pHead = NULL;
PLIST_ENTRY pNext = NULL;
PKEY_DATA pFindData = NULL;
if (!pwszValueName || !pOpenKey) {
DPFN(eDbgLevelError, "[FindValueNameInList] Invalid parameter(s)");
return NULL;
}
pHead = &pOpenKey->KeyData;
pNext = pHead->Flink;
while (pNext != pHead) {
pFindData = CONTAINING_RECORD(pNext, KEY_DATA, Entry);
if (!_wcsicmp(pwszValueName, pFindData->wszValueName)) {
fFound = TRUE;
break;
}
pNext = pNext->Flink;
}
return (fFound ? pFindData : NULL);
}
/*++
Given a key path, attempt to locate it in the list.
This function may not always return a pointer.
--*/
PLOG_OPEN_KEY
CLogRegistry::FindKeyPathInList(
IN LPCWSTR pwszKeyPath
)
{
BOOL fFound = FALSE;
PLIST_ENTRY pCurrent = NULL;
PLOG_OPEN_KEY pFindKey = NULL;
if (!pwszKeyPath) {
DPFN(eDbgLevelError, "[FindKeyPathInList] Invalid parameter");
return NULL;
}
//
// Attempt to locate the entry in the list.
//
pCurrent = g_OpenKeyListHead.Flink;
while (pCurrent != &g_OpenKeyListHead) {
pFindKey = CONTAINING_RECORD(pCurrent, LOG_OPEN_KEY, Entry);
if (pFindKey->pwszFullKeyPath) {
if (!_wcsicmp(pwszKeyPath, pFindKey->pwszFullKeyPath)) {
fFound = TRUE;
break;
}
}
pCurrent = pCurrent->Flink;
}
return (fFound ? pFindKey : NULL);
}
/*++
Given a key handle, remove it from the array in the list.
--*/
PLOG_OPEN_KEY
CLogRegistry::RemoveKeyHandleFromArray(
IN HKEY hKey
)
{
UINT uCount;
PLIST_ENTRY pCurrent = NULL;
PLOG_OPEN_KEY pFindKey = NULL;
if (!hKey) {
DPFN(eDbgLevelError,
"[RemoveKeyHandleFromArray] Invalid key handle passed!");
return NULL;
}
pCurrent = g_OpenKeyListHead.Flink;
while (pCurrent != &g_OpenKeyListHead) {
pFindKey = CONTAINING_RECORD(pCurrent, LOG_OPEN_KEY, Entry);
//
// Step through this guy's array looking for the handle.
//
for (uCount = 0; uCount < pFindKey->cHandles; uCount++) {
//
// If we find the handle, set the array element to NULL and
// decrement the count of handles for this entry.
//
if (pFindKey->hKeyBase[uCount] == hKey) {
DPFN(eDbgLevelInfo,
"[RemoveKeyHandleFromArray] Removing handle 0x%08X",
hKey);
pFindKey->hKeyBase[uCount] = NULL;
pFindKey->cHandles--;
return pFindKey;
}
}
pCurrent = pCurrent->Flink;
}
return NULL;
}
/*++
Finds a key handle in the array.
--*/
PLOG_OPEN_KEY
CLogRegistry::FindKeyHandleInArray(
IN HKEY hKey
)
{
UINT uCount;
BOOL fFound = FALSE;
PLOG_OPEN_KEY pFindKey = NULL;
PLIST_ENTRY pCurrent = NULL;
if (!hKey) {
DPFN(eDbgLevelError,
"[FindKeyHandleInArray] Invalid key handle passed!");
return NULL;
}
pCurrent = g_OpenKeyListHead.Flink;
while (pCurrent != &g_OpenKeyListHead) {
pFindKey = CONTAINING_RECORD(pCurrent, LOG_OPEN_KEY, Entry);
//
// Step through this guy's array looking for the handle.
//
for (uCount = 0; uCount < pFindKey->cHandles; uCount++) {
if (pFindKey->hKeyBase[uCount] == hKey) {
fFound = TRUE;
break;
}
}
if (fFound) {
break;
}
pCurrent = pCurrent->Flink;
}
#if DBG
if (!fFound) {
//
// Dear God - the key handle is not in the list!
// Break into the debugger on checked builds.
//
DPFN(eDbgLevelError,
"[FindKeyHandleInArray] Key 0x%08X not in list!",
hKey);
PrintNameFromKey(hKey);
DbgBreakPoint();
}
#endif // DBG
return (fFound ? pFindKey : NULL);
}
/*++
Given a predefined handle and a subkey path,
open the key to force it into the list.
--*/
HKEY
CLogRegistry::ForceSubKeyIntoList(
IN HKEY hKeyPredefined,
IN LPCWSTR pwszSubKey
)
{
LONG lRetVal;
HKEY hKeyRet;
if (!pwszSubKey) {
DPFN(eDbgLevelError, "[ForceSubKeyIntoList] Invalid parameter");
return NULL;
}
lRetVal = OpenKeyExW(hKeyPredefined,
pwszSubKey,
0,
KEY_WRITE,
&hKeyRet);
if (ERROR_SUCCESS != lRetVal) {
DPFN(eDbgLevelError, "[ForceSubKeyIntoList] Failed to open key");
return NULL;
}
return hKeyRet;
}
/*++
Add a non-predefined key handle to the array.
--*/
PLOG_OPEN_KEY
CLogRegistry::AddKeyHandleToList(
IN HKEY hKey,
IN HKEY hKeyNew,
IN LPCWSTR pwszSubKeyPath,
IN BOOL fExisting
)
{
UINT uCount;
DWORD cchLen;
PLOG_OPEN_KEY pFindKey = NULL;
PLOG_OPEN_KEY pRetKey = NULL;
PLOG_OPEN_KEY pExistingFindKey = NULL;
//
// If hKeyNew, which is the key handle the caller received
// from the function, is a predefined handle, we simply
// call FindKeyInArray, which will return a pointer to the
// list entry that contains that key handle. These handles
// were added during initialization, so there's no chance
// that the caller won't get a pointer back.
//
if (IsPredefinedRegistryHandle(hKeyNew)) {
return FindKeyHandleInArray(hKeyNew);
}
//
// We've got a usual case where a key has been opened, and
// now the caller is opening a subkey underneath it.
//
pFindKey = FindKeyHandleInArray(hKey);
//
// If pFindKey ever comes back as NULL, something is really
// wrong. Every OpenKey/CreateKey comes through us and goes
// into the list (with the exception of predefined handles
// which are already stored there.) Dump out as much data
// as we can to figure out what went wrong.
//
if (!pFindKey) {
DPFN(eDbgLevelError,
"[AddKeyHandleToList] Key not found in list! Key Handle = 0x%08X New key = 0x%08X Path = %ls",
hKey, hKeyNew, pwszSubKeyPath);
return NULL;
}
pRetKey = (PLOG_OPEN_KEY)MemAlloc(sizeof(LOG_OPEN_KEY));
if (!pRetKey) {
DPFN(eDbgLevelError, "[AddKeyHandleToList] No memory available");
return NULL;
}
//
// Make room for the subkey path that will go into the new
// node. If the node that we found has a subkey path stored,
// take that into account also.
//
if (pwszSubKeyPath) {
cchLen = wcslen(pwszSubKeyPath);
}
if (pFindKey->pwszSubKeyPath) {
cchLen += wcslen(pFindKey->pwszSubKeyPath);
}
if (pFindKey->pwszSubKeyPath || pwszSubKeyPath) {
cchLen += 2;
pRetKey->pwszSubKeyPath = (LPWSTR)MemAlloc(cchLen * sizeof(WCHAR));
if (!pRetKey->pwszSubKeyPath) {
DPFN(eDbgLevelError, "[AddKeyHandleToList] No memory for subkey path");
goto cleanup;
}
}
//
// If the node that we found has a subkey path, take it
// and copy it over to the new node and concatenate
// the subkey path that we got passed. Otherwise just
// store the path that was passed, if available.
//
if (pFindKey->pwszSubKeyPath && pwszSubKeyPath) {
StringCchCopy(pRetKey->pwszSubKeyPath, cchLen, pFindKey->pwszSubKeyPath);
StringCchCat(pRetKey->pwszSubKeyPath, cchLen, L"\\");
StringCchCat(pRetKey->pwszSubKeyPath, cchLen, pwszSubKeyPath);
} else if (pwszSubKeyPath) {
StringCchCopy(pRetKey->pwszSubKeyPath, cchLen, pwszSubKeyPath);
}
//
// Make room for the full key path. This will store a path
// of something like HKEY_LOCAL_MACHINE\Software\Microsoft...
// that will be used for logging purposes.
//
if (pRetKey->pwszSubKeyPath) {
cchLen = wcslen(pRetKey->pwszSubKeyPath);
}
cchLen += 2;
cchLen += MAX_ROOT_LENGTH;
pRetKey->pwszFullKeyPath = (LPWSTR)MemAlloc(cchLen * sizeof(WCHAR));
if (!pRetKey->pwszFullKeyPath) {
DPFN(eDbgLevelError,
"[AddKeyHandleToList] No memory for full key path");
goto cleanup;
}
//
// Convert the predefined handle to a string and store it in
// the node that we're about to add to the list.
//
if (!PredefinedKeyToString(pFindKey->hKeyRoot,
MAX_ROOT_LENGTH,
&pRetKey->pwszFullKeyPath)) {
DPFN(eDbgLevelError,
"[AddKeyHandleToList] PredefinedKey -> String failed");
goto cleanup;
}
if (pwszSubKeyPath) {
StringCchCat(pRetKey->pwszFullKeyPath, cchLen, L"\\");
StringCchCat(pRetKey->pwszFullKeyPath, cchLen, pRetKey->pwszSubKeyPath);
}
//
// At this point we have a full key path.
// We attempt to find the path in the linked list.
// If we find it, we're going to update the handle array and count for this guy.
// If we don't find it, we're going to add a new entry to the list.
//
pExistingFindKey = FindKeyPathInList(pRetKey->pwszFullKeyPath);
if (!pExistingFindKey) {
//
// Fill in information about this key and add it to the list.
//
pRetKey->hKeyBase[0] = hKeyNew;
pRetKey->hKeyRoot = pFindKey->hKeyRoot;
pRetKey->cHandles = 1;
pRetKey->dwFlags |= fExisting ? LRC_EXISTING_KEY : 0;
InitializeListHead(&pRetKey->KeyData);
DPFN(eDbgLevelInfo, "[AddKeyHandleToList] Adding key: %p", pRetKey);
InsertHeadList(&g_OpenKeyListHead, &pRetKey->Entry);
return pRetKey;
} else {
//
// Store this handle in the array and increment the handle count.
// Make sure we don't overstep the array bounds.
//
for (uCount = 0; uCount < pExistingFindKey->cHandles; uCount++) {
if (NULL == pExistingFindKey->hKeyBase[uCount]) {
break;
}
}
if (uCount >= MAX_NUM_HANDLES) {
DPFN(eDbgLevelError, "[AddKeyHandleToList] Handle count reached");
goto cleanup;
}
pExistingFindKey->hKeyBase[uCount] = hKeyNew;
pExistingFindKey->dwFlags &= ~LRC_DELETED_KEY;
pExistingFindKey->cHandles++;
}
cleanup:
if (pRetKey->pwszFullKeyPath) {
MemFree(pRetKey->pwszFullKeyPath);
}
if (pRetKey) {
MemFree(pRetKey);
}
return pExistingFindKey;
}
/*++
Add a value to the list.
--*/
PKEY_DATA
CLogRegistry::AddValueNameToList(
IN PLOG_OPEN_KEY pLogOpenKey,
IN LPCWSTR pwszValueName
)
{
PKEY_DATA pKeyData = NULL;
pKeyData = (PKEY_DATA)MemAlloc(sizeof(KEY_DATA));
if (!pKeyData) {
DPFN(eDbgLevelError, "[AddValueNameToList] Failed to allocate memory");
return NULL;
}
if (!GetOriginalDataForKey(pLogOpenKey, pKeyData, pwszValueName)) {
DPFN(eDbgLevelError,
"[AddValueNameToList] Failed to get original data");
goto cleanup;
}
if (pwszValueName) {
StringCchCopy(pKeyData->wszValueName,
ARRAYSIZE(pKeyData->wszValueName),
pwszValueName);
} else {
//
// If the valuename is NULL, assign our unique id.
//
StringCchCopy(pKeyData->wszValueName,
ARRAYSIZE(pKeyData->wszValueName),
g_wszUniqueId);
}
InsertHeadList(&pLogOpenKey->KeyData, &pKeyData->Entry);
return pKeyData;
cleanup:
if (pKeyData) {
MemFree(pKeyData);
}
return NULL;
}
/*++
The entry point for modifying the linked list data.
--*/
PLOG_OPEN_KEY
CLogRegistry::UpdateKeyList(
IN HKEY hKeyRoot,
IN HKEY hKeyNew,
IN LPCWSTR pwszSubKey,
IN LPCWSTR pwszValueName,
IN BOOL fExisting,
IN UpdateType eType
)
{
PKEY_DATA pKeyData = NULL;
PLOG_OPEN_KEY pRetKey = NULL;
switch (eType) {
case eAddKeyHandle:
pRetKey = AddKeyHandleToList(hKeyRoot, hKeyNew, pwszSubKey, fExisting);
break;
case eRemoveKeyHandle:
pRetKey = RemoveKeyHandleFromArray(hKeyNew);
break;
case eStartModifyValue:
case eStartDeleteValue:
pRetKey = FindKeyHandleInArray(hKeyNew);
if (!pRetKey) {
DPFN(eDbgLevelError,
"[UpdateKeyList] Start Modify: Failed to find handle in array");
break;
}
if (!pwszValueName) {
pKeyData = FindValueNameInList(g_wszUniqueId, pRetKey);
} else {
pKeyData = FindValueNameInList(pwszValueName, pRetKey);
}
if (pKeyData) {
//
// If the caller is attempting to modify the value, and we've
// already gotten data for it, don't do it again.
// Also, if they're attempting to delete the value, and it's
// been modified, don't do it again.
//
if ((pKeyData->pOriginalData || pKeyData->pFinalData) ||
(pKeyData->dwFlags & LRC_MODIFIED_VALUE) &&
(eStartDeleteValue == eType)) {
break;
}
if (!GetOriginalDataForKey(pRetKey, pKeyData, pwszValueName)) {
DPFN(eDbgLevelError,
"[UpdateKeyList] Start Modify: Failed to get original data");
break;
}
} else {
//
// We've never seen this value before. Insert it into the list.
//
if (!AddValueNameToList(pRetKey, pwszValueName)) {
DPFN(eDbgLevelError,
"[UpdateKeyList] Start Modify: Failed to insert value");
break;
}
}
break;
case eEndModifyValue:
case eEndDeleteValue:
pRetKey = FindKeyHandleInArray(hKeyNew);
if (!pRetKey) {
DPFN(eDbgLevelError,
"[UpdateKeyList] End Modify: Failed to find handle in array");
break;
}
if (!pwszValueName) {
pKeyData = FindValueNameInList(g_wszUniqueId, pRetKey);
} else {
pKeyData = FindValueNameInList(pwszValueName, pRetKey);
}
if (!pKeyData) {
DPFN(eDbgLevelError,
"[UpdateKeyList] End Modify: Failed to find value in list");
break;
}
if (eEndModifyValue == eType) {
if (!GetFinalDataForKey(pRetKey, pKeyData, pwszValueName)) {
DPFN(eDbgLevelError,
"[UpdateKeyList] End Modify: Failed to get final data");
break;
}
pKeyData->dwFlags |= LRC_MODIFIED_VALUE;
}
else if (eEndDeleteValue == eType) {
pKeyData->dwFlags |= LRC_DELETED_VALUE;
}
break;
case eDeletedKey:
pRetKey = FindKeyHandleInArray(hKeyNew);
if (!pRetKey) {
DPFN(eDbgLevelError,
"[UpdateKeyList] DeleteKey: Failed to find handle in array");
break;
}
pRetKey->dwFlags |= LRC_DELETED_KEY;
break;
default:
DPFN(eDbgLevelError, "[UpdateKeyList] Invalid enum type!");
break;
}
return pRetKey;
}
/*++
Formats the data to form an XML element and logs it.
--*/
void
FormatKeyDataIntoElement(
IN LPCWSTR pwszOperation,
IN PLOG_OPEN_KEY pLogOpenKey
)
{
UINT cbSize;
PVOID pTemp = NULL;
HRESULT hr;
if (!pLogOpenKey) {
DPFN(eDbgLevelError, "[FormatKeyDataIntoElement] Invalid argument");
return;
}
//
// To make it easier to replace & ' " < and > with their
// XML entities, we convert the data to CString.
//
CString csFullKeyPath(pLogOpenKey->pwszFullKeyPath);
csFullKeyPath.Replace(L"&", L"&amp;");
csFullKeyPath.Replace(L"<", L"&lt;");
csFullKeyPath.Replace(L">", L"&gt;");
csFullKeyPath.Replace(L"'", L"&apos;");
csFullKeyPath.Replace(L"\"", L"&quot;");
//
// To keep allocations to a minimum, we allocate a global
// buffer one time, then reallocate if the data we're
// logging is larger than the buffer.
//
if (g_cbTempBufferSize == 0) {
g_pwszTempBuffer = (LPWSTR)MemAlloc(TEMP_BUFFER_SIZE * sizeof(WCHAR));
if (!g_pwszTempBuffer) {
DPFN(eDbgLevelError,
"[FormatKeyDataIntoElement] Failed to allocate memory");
return;
}
g_cbTempBufferSize = TEMP_BUFFER_SIZE * sizeof(WCHAR);
}
*g_pwszTempBuffer = 0;
//
// Determine how large of a buffer we'll need.
//
cbSize = csFullKeyPath.GetLength();
cbSize += MAX_OPERATION_LENGTH;
cbSize += KEY_ELEMENT_SIZE;
cbSize *= sizeof(WCHAR);
if (cbSize > g_cbTempBufferSize) {
//
// Our global buffer is not large enough; reallocate.
//
pTemp = (LPWSTR)MemReAlloc(g_pwszTempBuffer,
cbSize + BUFFER_ALLOCATION_DELTA);
if (pTemp) {
g_pwszTempBuffer = (LPWSTR)pTemp;
} else {
DPFN(eDbgLevelError,
"[FormatKeyDataIntoElement] Failed to reallocate memory");
return;
}
g_cbTempBufferSize = cbSize + BUFFER_ALLOCATION_DELTA;
}
hr = StringCbPrintf(g_pwszTempBuffer,
g_cbTempBufferSize,
L" <OPERATION TYPE=\"%ls\" KEY_PATH=\"%ls\"/>\r\n",
pwszOperation,
csFullKeyPath.Get());
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[FormatKeyDataIntoElement] 0x%08X Failed to format data",
HRESULT_CODE(hr));
return;
}
WriteEntryToLog(g_pwszTempBuffer);
}
void
FormatValueDataIntoElement(
IN CString& csFullKeyPath,
IN LPCWSTR pwszOperation,
IN LPCWSTR pwszValueName,
IN LPCWSTR pwszOriginalValueType,
IN LPCWSTR pwszFinalValueType
)
{
UINT cbSize;
WCHAR* pwszEnd = NULL;
size_t cbRemaining;
PVOID pTemp = NULL;
HRESULT hr;
//
// To keep allocations to a minimum, we allocate a global
// buffer one time, then reallocate if the data we're
// logging is larger than the buffer.
//
if (!g_cbTempBufferSize) {
g_pwszTempBuffer = (LPWSTR)MemAlloc(TEMP_BUFFER_SIZE * sizeof(WCHAR));
if (!g_pwszTempBuffer) {
DPFN(eDbgLevelError,
"[FormatValueDataIntoElement] Failed to allocate memory");
return;
}
g_cbTempBufferSize = TEMP_BUFFER_SIZE * sizeof(WCHAR);
}
//
// Determine how large of a buffer we'll need.
//
cbSize = wcslen(pwszOperation);
cbSize += wcslen(pwszOriginalValueType);
cbSize += wcslen(pwszFinalValueType);
cbSize += wcslen(g_pwszOriginalData);
cbSize += wcslen(g_pwszFinalData);
cbSize += csFullKeyPath.GetLength();
cbSize += VALUE_ELEMENT_SIZE;
if (pwszValueName) {
cbSize += wcslen(pwszValueName);
}
cbSize *= sizeof(WCHAR);
if (cbSize > g_cbTempBufferSize) {
//
// Our global buffer is not large enough; reallocate.
//
pTemp = (LPWSTR)MemReAlloc(g_pwszTempBuffer,
cbSize + BUFFER_ALLOCATION_DELTA);
if (pTemp) {
g_pwszTempBuffer = (LPWSTR)pTemp;
} else {
DPFN(eDbgLevelError,
"[FormatValueDataIntoElement] Failed to reallocate memory");
return;
}
g_cbTempBufferSize = cbSize + BUFFER_ALLOCATION_DELTA;
}
//
// Open the <OPERATION> element.
//
hr = StringCbPrintfEx(g_pwszTempBuffer,
g_cbTempBufferSize,
&pwszEnd,
&cbRemaining,
0,
L" <OPERATION TYPE=\"%ls\" KEY_PATH=\"%ls\">\r\n",
pwszOperation,
csFullKeyPath.Get());
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[FormatValueDataIntoElement] 0x%08X Failed to format OPERATION",
HRESULT_CODE(hr));
return;
}
//
// Write the <VALUE_NAME> element.
//
if (pwszValueName) {
hr = StringCbPrintfEx(pwszEnd,
cbRemaining,
&pwszEnd,
&cbRemaining,
0,
L" <VALUE_NAME><![CDATA[%ls]]></VALUE_NAME>\r\n",
pwszValueName);
} else {
hr = StringCbPrintfEx(pwszEnd,
cbRemaining,
&pwszEnd,
&cbRemaining,
0,
L" <VALUE_NAME/>\r\n");
}
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[FormatValueDataIntoElement] 0x%08X Failed to format VALUE_NAME",
HRESULT_CODE(hr));
return;
}
//
// Write the <ORIGINAL_DATA> element.
//
if (g_pwszOriginalData[0]) {
hr = StringCbPrintfEx(pwszEnd,
cbRemaining,
&pwszEnd,
&cbRemaining,
0,
L" <ORIGINAL_DATA TYPE=\"%ls\"><![CDATA[%ls]]></ORIGINAL_DATA>\r\n",
pwszOriginalValueType,
g_pwszOriginalData);
} else {
hr = StringCbPrintfEx(pwszEnd,
cbRemaining,
&pwszEnd,
&cbRemaining,
0,
L" <ORIGINAL_DATA/>\r\n");
}
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[FormatValueDataIntoElement] 0x%08X Failed to format ORIGINAL_DATA",
HRESULT_CODE(hr));
return;
}
//
// Write the <FINAL_DATA> element.
//
if (g_pwszFinalData[0]) {
hr = StringCbPrintfEx(pwszEnd,
cbRemaining,
&pwszEnd,
&cbRemaining,
0,
L" <FINAL_DATA TYPE=\"%ls\"><![CDATA[%ls]]></FINAL_DATA>\r\n",
pwszFinalValueType,
g_pwszFinalData);
} else {
hr = StringCbPrintfEx(pwszEnd,
cbRemaining,
&pwszEnd,
&cbRemaining,
0,
L" <FINAL_DATA/>\r\n");
}
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[FormatValueDataIntoElement] 0x%08X Failed to format FINAL_DATA",
HRESULT_CODE(hr));
return;
}
//
// Close the <OPERATION> element.
//
hr = StringCbPrintf(pwszEnd,
cbRemaining,
L" </OPERATION>\r\n");
if (FAILED(hr)) {
DPFN(eDbgLevelError,
"[FormatValueDataIntoElement] 0x%08X Failed to close OPERATION",
HRESULT_CODE(hr));
return;
}
WriteEntryToLog(g_pwszTempBuffer);
}
/*++
Converts binary data into a readable string.
--*/
void
ExtractBinaryData(
IN PVOID pBinary,
IN DWORD cbDataSize,
IN DWORD cbOutBufferSize,
OUT LPWSTR* pwszString
)
{
size_t cbRemaining;
WCHAR* pwszEnd = NULL;
PBYTE pByte = NULL;
DWORD dwLoop = 0;
if (!pBinary || !pwszString) {
DPFN(eDbgLevelError, "[ExtractBinaryData] Invalid parameter(s)");
return;
}
//
// Point to the data and determine how many times we need to loop.
//
pByte = (BYTE*)pBinary;
dwLoop = cbDataSize / sizeof(WCHAR);
//
// Initialize the count of characters remaining and the pointer
// to our destination string. This has to occur outside of the
// loop.
//
cbRemaining = cbOutBufferSize;
pwszEnd = *pwszString;
while (dwLoop) {
StringCbPrintfEx(pwszEnd,
cbRemaining,
&pwszEnd,
&cbRemaining,
0,
L"%lx",
*pByte++);
dwLoop--;
}
}
/*++
Converts a REG_MULTI_SZ to a readable string.
--*/
void
ExtractMultiSzStrings(
IN PVOID pMultiSz,
IN DWORD cbOutBufferSize,
OUT LPWSTR* pwszString
)
{
size_t cbRemaining;
UINT uSize;
WCHAR* pwszEnd = NULL;
LPWSTR pwszTmp = NULL;
if (!pMultiSz || !pwszString) {
DPFN(eDbgLevelError, "[ExtractMultiSzStrings] Invalid parameter(s)");
return;
}
//
// Walk the list of NULL-terminated strings and put them in the buffer.
//
pwszTmp = (LPWSTR)pMultiSz;
cbRemaining = cbOutBufferSize;
pwszEnd = *pwszString;
while (TRUE) {
StringCbPrintfEx(pwszEnd,
cbRemaining,
&pwszEnd,
&cbRemaining,
0,
L" %ls",
pwszTmp);
uSize = wcslen(pwszTmp) + 1;
pwszTmp += uSize;
if (*pwszTmp == '\0') {
break;
}
}
}
/*++
Formats the value data to form an XML element and logs it.
--*/
void
FormatValueData(
IN LPCWSTR pwszOperation,
IN PKEY_DATA pKeyData,
IN PLOG_OPEN_KEY pLogOpenKey
)
{
WCHAR wszFinalValueType[MAX_DATA_TYPE_LENGTH];
WCHAR wszOriginalValueType[MAX_DATA_TYPE_LENGTH];
LPCWSTR pwszValueName = NULL;
PVOID pOriginalTmp = NULL;
PVOID pFinalTmp = NULL;
//
// If we haven't already, allocate buffers that we'll
// use and reuse when getting original and final data.
//
if (!g_cbOriginalDataBufferSize) {
g_pwszOriginalData = (LPWSTR)MemAlloc(TEMP_BUFFER_SIZE * sizeof(WCHAR));
if (!g_pwszOriginalData) {
DPFN(eDbgLevelError,
"[FormatValueData] Failed to allocate memory for old data");
return;
}
g_cbOriginalDataBufferSize = TEMP_BUFFER_SIZE * sizeof(WCHAR);
}
if (!g_cbFinalDataBufferSize) {
g_pwszFinalData = (LPWSTR)MemAlloc(TEMP_BUFFER_SIZE * sizeof(WCHAR));
if (!g_pwszFinalData) {
DPFN(eDbgLevelError,
"[FormatValueData] Failed to allocate memory for new data");
return;
}
g_cbFinalDataBufferSize = TEMP_BUFFER_SIZE * sizeof(WCHAR);
}
*g_pwszOriginalData = 0;
*g_pwszFinalData = 0;
//
// To make it easier to replace & ' " < and > with their
// XML entities, we convert the data to CString.
//
CString csFullKeyPath(pLogOpenKey->pwszFullKeyPath);
csFullKeyPath.Replace(L"&", L"&amp;");
csFullKeyPath.Replace(L"<", L"&lt;");
csFullKeyPath.Replace(L">", L"&gt;");
csFullKeyPath.Replace(L"'", L"&apos;");
csFullKeyPath.Replace(L"\"", L"&quot;");
switch (pKeyData->dwOriginalValueType) {
case REG_SZ:
StringCchCopy(wszOriginalValueType,
ARRAYSIZE(wszOriginalValueType),
L"REG_SZ");
break;
case REG_EXPAND_SZ:
StringCchCopy(wszOriginalValueType,
ARRAYSIZE(wszOriginalValueType),
L"REG_EXPAND_SZ");
break;
case REG_MULTI_SZ:
StringCchCopy(wszOriginalValueType,
ARRAYSIZE(wszOriginalValueType),
L"REG_MULTI_SZ");
break;
case REG_DWORD:
StringCchCopy(wszOriginalValueType,
ARRAYSIZE(wszOriginalValueType),
L"REG_DWORD");
break;
case REG_BINARY:
StringCchCopy(wszOriginalValueType,
ARRAYSIZE(wszOriginalValueType),
L"REG_BINARY");
break;
default:
StringCchCopy(wszOriginalValueType,
ARRAYSIZE(wszOriginalValueType),
L"Unknown");
break;
}
switch (pKeyData->dwFinalValueType) {
case REG_SZ:
StringCchCopy(wszFinalValueType,
ARRAYSIZE(wszFinalValueType),
L"REG_SZ");
break;
case REG_EXPAND_SZ:
StringCchCopy(wszFinalValueType,
ARRAYSIZE(wszFinalValueType),
L"REG_EXPAND_SZ");
break;
case REG_MULTI_SZ:
StringCchCopy(wszFinalValueType,
ARRAYSIZE(wszFinalValueType),
L"REG_MULTI_SZ");
break;
case REG_DWORD:
StringCchCopy(wszFinalValueType,
ARRAYSIZE(wszFinalValueType),
L"REG_DWORD");
break;
case REG_BINARY:
StringCchCopy(wszFinalValueType,
ARRAYSIZE(wszFinalValueType),
L"REG_BINARY");
break;
default:
StringCchCopy(wszFinalValueType,
ARRAYSIZE(wszFinalValueType),
L"Unknown");
break;
}
//
// If our temporary buffers are not large enough to store the data, reallocate.
//
if (pKeyData->cbOriginalDataSize > g_cbOriginalDataBufferSize) {
pOriginalTmp = (LPWSTR)MemReAlloc(g_pwszOriginalData,
pKeyData->cbOriginalDataSize + BUFFER_ALLOCATION_DELTA);
if (pOriginalTmp) {
g_pwszOriginalData = (LPWSTR)pOriginalTmp;
} else {
DPFN(eDbgLevelError,
"[FormatValueData] Failed to reallocate for original data");
return;
}
g_cbOriginalDataBufferSize = pKeyData->cbOriginalDataSize + BUFFER_ALLOCATION_DELTA;
}
if (pKeyData->cbFinalDataSize > g_cbFinalDataBufferSize) {
pFinalTmp = (LPWSTR)MemReAlloc(g_pwszFinalData,
pKeyData->cbFinalDataSize + BUFFER_ALLOCATION_DELTA);
if (pFinalTmp) {
g_pwszFinalData = (LPWSTR)pFinalTmp;
} else {
DPFN(eDbgLevelError,
"[FormatValueData] Failed to reallocate for new data");
return;
}
g_cbFinalDataBufferSize = pKeyData->cbFinalDataSize + BUFFER_ALLOCATION_DELTA;
}
//
// Store the original and new data in the buffers.
// Note that operations are performed differently based on the data type.
//
if (pKeyData->pOriginalData) {
switch (pKeyData->dwOriginalValueType) {
case REG_DWORD:
StringCbPrintf(g_pwszOriginalData,
g_cbOriginalDataBufferSize,
L"%lu",
(*(DWORD*)pKeyData->pOriginalData));
break;
case REG_SZ:
case REG_EXPAND_SZ:
StringCbCopy(g_pwszOriginalData,
g_cbOriginalDataBufferSize,
(const WCHAR*)pKeyData->pOriginalData);
break;
case REG_MULTI_SZ:
ExtractMultiSzStrings(pKeyData->pOriginalData,
g_cbOriginalDataBufferSize,
&g_pwszOriginalData);
break;
case REG_BINARY:
ExtractBinaryData(pKeyData->pOriginalData,
pKeyData->cbOriginalDataSize,
g_cbOriginalDataBufferSize,
&g_pwszOriginalData);
break;
default:
DPFN(eDbgLevelError, "[FormatValueData] Unsupported value type");
break;
}
}
if (pKeyData->pFinalData) {
switch (pKeyData->dwFinalValueType) {
case REG_DWORD:
StringCbPrintf(g_pwszFinalData,
g_cbFinalDataBufferSize,
L"%lu",
(*(DWORD*)pKeyData->pFinalData));
break;
case REG_SZ:
case REG_EXPAND_SZ:
StringCbCopy(g_pwszFinalData,
g_cbFinalDataBufferSize,
(const WCHAR*)pKeyData->pFinalData);
break;
case REG_MULTI_SZ:
ExtractMultiSzStrings(pKeyData->pFinalData,
g_cbFinalDataBufferSize,
&g_pwszFinalData);
break;
case REG_BINARY:
ExtractBinaryData(pKeyData->pFinalData,
pKeyData->cbFinalDataSize,
g_cbFinalDataBufferSize,
&g_pwszFinalData);
break;
default:
DPFN(eDbgLevelError, "[FormatValueData] Unsupported value type");
break;
}
}
//
// Ensure that our unique id doesn't show up in the log.
//
if (_wcsicmp(pKeyData->wszValueName, g_wszUniqueId)) {
pwszValueName = pKeyData->wszValueName;
}
//
// Put the data into an XML element and log it.
//
FormatValueDataIntoElement(csFullKeyPath,
pwszOperation,
pwszValueName,
wszOriginalValueType,
wszFinalValueType);
}
/*++
Determines the changes that took place on the specified key and
if applicable, writes it to the log.
--*/
BOOL
EvaluateKeyChanges(
IN PLOG_OPEN_KEY pLogOpenKey
)
{
if (!pLogOpenKey) {
DPFN(eDbgLevelError, "[EvaluateKeyChanges] Invalid parameter");
return FALSE;
}
//
// 1. Check for deletion of an existing key.
//
if ((pLogOpenKey->dwFlags & LRC_DELETED_KEY) &&
(pLogOpenKey->dwFlags & LRC_EXISTING_KEY)) {
FormatKeyDataIntoElement(L"Deleted Key", pLogOpenKey);
return TRUE;
}
//
// 2. Check for creation of a new key.
//
if (!(pLogOpenKey->dwFlags & LRC_EXISTING_KEY) &&
(!(pLogOpenKey->dwFlags & LRC_DELETED_KEY))) {
FormatKeyDataIntoElement(L"Created Key", pLogOpenKey);
return TRUE;
}
//
// 3. Check for deletion of a non-existing key.
// This is an indicator that we should not look for
// changes to values below this key.
//
if (pLogOpenKey->dwFlags & LRC_DELETED_KEY) {
return FALSE;
}
return TRUE;
}
/*++
Determines the changes that took place on the specified value and
if applicable, writes it to the log.
--*/
void
EvaluateValueChanges(
IN PKEY_DATA pKeyData,
IN PLOG_OPEN_KEY pLogOpenKey
)
{
if (!pKeyData || !pLogOpenKey) {
DPFN(eDbgLevelError, "[EvaluateValueChanges] Invalid parameter(s)");
return;
}
//
// 1. Check for deletion of an existing value.
//
if ((pKeyData->dwFlags & LRC_DELETED_VALUE) &&
(pKeyData->dwFlags & LRC_EXISTING_VALUE)) {
FormatValueData(L"Deleted Value", pKeyData, pLogOpenKey);
return;
}
//
// 2. Check for modification of an existing value.
//
if ((pKeyData->dwFlags & LRC_EXISTING_VALUE) &&
(pKeyData->dwFlags & LRC_MODIFIED_VALUE)) {
FormatValueData(L"Modified Value", pKeyData, pLogOpenKey);
return;
}
//
// 3. Check for creation of a new value value.
//
if ((pKeyData->dwFlags & LRC_MODIFIED_VALUE) &&
(!(pKeyData->dwFlags & LRC_DELETED_VALUE) &&
(!(pKeyData->dwFlags & LRC_EXISTING_VALUE)))) {
FormatValueData(L"Created Value", pKeyData, pLogOpenKey);
return;
}
}
/*++
Write the entire linked list out to the log file.
--*/
BOOL
WriteListToLogFile(
void
)
{
PLIST_ENTRY pKeyNext = NULL;
PLIST_ENTRY pValueHead = NULL;
PLIST_ENTRY pValueNext = NULL;
PKEY_DATA pKeyData = NULL;
PLOG_OPEN_KEY pOpenKey = NULL;
//
// Write out modifications for keys.
//
pKeyNext = g_OpenKeyListHead.Blink;
while (pKeyNext != &g_OpenKeyListHead) {
pOpenKey = CONTAINING_RECORD(pKeyNext, LOG_OPEN_KEY, Entry);
//
// EvaluateKeyChanges will return TRUE if the key was not
// deleted. If this is the case, continue the search and
// evaluate changes to values within this key.
//
if (EvaluateKeyChanges(pOpenKey)) {
//
// Write out modifications for values.
//
pValueHead = &pOpenKey->KeyData;
pValueNext = pValueHead->Blink;
while (pValueNext != pValueHead) {
pKeyData = CONTAINING_RECORD(pValueNext, KEY_DATA, Entry);
EvaluateValueChanges(pKeyData, pOpenKey);
pValueNext = pValueNext->Blink;
}
}
pKeyNext = pKeyNext->Blink;
}
CloseLogFile();
return TRUE;
}
//
// Begin implementation of the class.
//
LONG
CLogRegistry::CreateKeyExA(
HKEY hKey,
LPCSTR pszSubKey,
DWORD Reserved,
LPSTR pszClass,
DWORD dwOptions,
REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
PHKEY phkResult,
LPDWORD lpdwDisposition
)
{
LPWSTR pwszSubKey = NULL;
LPWSTR pwszClass = NULL;
LONG lRetVal;
//
// Stub out to CreateKeyExW.
//
if (pszSubKey) {
if (!ConvertAnsiToUnicode(pszSubKey, &pwszSubKey)) {
DPFN(eDbgLevelError, "[CreateKeyExA] Ansi -> Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
if (pszClass) {
if (!ConvertAnsiToUnicode(pszClass, &pwszClass)) {
DPFN(eDbgLevelError, "[CreateKeyExA] Ansi to Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
lRetVal = CreateKeyExW(
hKey,
pwszSubKey,
Reserved,
pwszClass,
dwOptions,
samDesired,
lpSecurityAttributes,
phkResult,
lpdwDisposition);
if (pwszSubKey) {
MemFree(pwszSubKey);
}
if (pwszClass) {
MemFree(pwszClass);
}
return lRetVal;
}
LONG
CLogRegistry::CreateKeyExW(
HKEY hKey,
LPCWSTR pwszSubKey,
DWORD Reserved,
LPWSTR pwszClass,
DWORD dwOptions,
REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
PHKEY phkResult,
LPDWORD lpdwDisposition
)
{
DWORD dwDisposition = 0;
LONG lRetVal;
BOOL fExisting = FALSE;
if (!lpdwDisposition) {
lpdwDisposition = &dwDisposition;
}
lRetVal = ORIGINAL_API(RegCreateKeyExW)(
hKey,
pwszSubKey,
Reserved,
pwszClass,
dwOptions,
samDesired,
lpSecurityAttributes,
phkResult,
lpdwDisposition);
if (lRetVal == ERROR_SUCCESS) {
if (REG_OPENED_EXISTING_KEY == *lpdwDisposition) {
fExisting = TRUE;
}
UpdateKeyList(hKey,
*phkResult,
pwszSubKey,
NULL,
fExisting,
eAddKeyHandle);
}
return lRetVal;
}
LONG
CLogRegistry::OpenKeyExA(
HKEY hKey,
LPCSTR pszSubKey,
DWORD ulOptions,
REGSAM samDesired,
PHKEY phkResult
)
{
LPWSTR pwszSubKey = NULL;
LONG lRetVal;
//
// Stub out to OpenKeyExW.
//
if (pszSubKey) {
if (!ConvertAnsiToUnicode(pszSubKey, &pwszSubKey)) {
DPFN(eDbgLevelError, "[OpenKeyExA] Ansi -> Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
lRetVal = OpenKeyExW(
hKey,
pwszSubKey,
ulOptions,
samDesired,
phkResult);
if (pwszSubKey) {
MemFree(pwszSubKey);
}
return lRetVal;
}
LONG
CLogRegistry::OpenKeyExW(
HKEY hKey,
LPCWSTR pwszSubKey,
DWORD ulOptions,
REGSAM samDesired,
PHKEY phkResult
)
{
LONG lRetVal;
lRetVal = ORIGINAL_API(RegOpenKeyExW)(
hKey,
pwszSubKey,
ulOptions,
samDesired,
phkResult);
if (lRetVal == ERROR_SUCCESS) {
UpdateKeyList(hKey,
*phkResult,
pwszSubKey,
NULL,
TRUE,
eAddKeyHandle);
}
return lRetVal;
}
LONG
CLogRegistry::OpenCurrentUser(
REGSAM samDesired,
PHKEY phkResult
)
{
LONG lRetVal;
lRetVal = ORIGINAL_API(RegOpenCurrentUser)(
samDesired,
phkResult);
if (lRetVal == ERROR_SUCCESS) {
UpdateKeyList(HKEY_CURRENT_USER,
*phkResult,
NULL,
NULL,
TRUE,
eAddKeyHandle);
}
return lRetVal;
}
LONG
CLogRegistry::OpenUserClassesRoot(
HANDLE hToken,
DWORD dwOptions,
REGSAM samDesired,
PHKEY phkResult
)
{
LONG lRetVal;
lRetVal = ORIGINAL_API(RegOpenUserClassesRoot)(
hToken,
dwOptions,
samDesired,
phkResult);
if (lRetVal == ERROR_SUCCESS) {
UpdateKeyList(HKEY_CLASSES_ROOT,
*phkResult,
NULL,
NULL,
TRUE,
eAddKeyHandle);
}
return lRetVal;
}
LONG
CLogRegistry::SetValueA(
HKEY hKey,
LPCSTR pszSubKey,
DWORD dwType,
LPCSTR lpData,
DWORD cbData
)
{
LPWSTR pwszSubKey = NULL;
LPWSTR pwszData = NULL;
LONG lRetVal;
//
// Stub out to SetValueW.
//
if (pszSubKey) {
if (!ConvertAnsiToUnicode(pszSubKey, &pwszSubKey)) {
DPFN(eDbgLevelError, "[SetValueA] Ansi -> Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
if (lpData) {
if (!ConvertAnsiToUnicode(lpData, &pwszData)) {
DPFN(eDbgLevelError, "[SetValueA] Ansi to Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
lRetVal = SetValueW(
hKey,
pwszSubKey,
dwType,
pwszData,
cbData * sizeof(WCHAR));
if (pwszSubKey) {
MemFree(pwszSubKey);
}
if (pwszData) {
MemFree(pwszData);
}
return lRetVal;
}
LONG
CLogRegistry::SetValueW(
HKEY hKey,
LPCWSTR pwszSubKey,
DWORD dwType,
LPCWSTR lpData,
DWORD cbData
)
{
HKEY hKeyLocal;
LONG lRetVal;
//
// Call OpenKeyEx to force this key to be added to the list.
//
if (pwszSubKey) {
lRetVal = OpenKeyExW(hKey,
pwszSubKey,
0,
KEY_SET_VALUE,
&hKeyLocal);
if (ERROR_SUCCESS != lRetVal) {
DPFN(eDbgLevelError, "[SetValueW] Failed to open key");
return lRetVal;
}
lRetVal = SetValueExW(hKeyLocal,
NULL,
0,
dwType,
(const BYTE*)lpData,
cbData);
CloseKey(hKeyLocal);
return lRetVal;
}
//
// All other cases will be handled properly.
//
lRetVal = SetValueExW(hKey,
NULL,
0,
dwType,
(const BYTE*)lpData,
cbData);
return lRetVal;
}
LONG
CLogRegistry::SetValueExA(
HKEY hKey,
LPCSTR pszValueName,
DWORD Reserved,
DWORD dwType,
CONST BYTE* lpData,
DWORD cbData
)
{
LPWSTR pwszData = NULL;
LPWSTR pwszValueName = NULL;
LONG lRetVal;
BOOL fString = FALSE;
//
// Stub out to SetValueExW.
//
if (pszValueName) {
if (!ConvertAnsiToUnicode(pszValueName, &pwszValueName)) {
DPFN(eDbgLevelError, "[SetValueExA] Ansi -> Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
if (REG_SZ == dwType || REG_EXPAND_SZ == dwType || REG_MULTI_SZ == dwType) {
fString = TRUE;
}
//
// If the data is of type string, convert it to Unicode.
//
if (lpData) {
if (REG_MULTI_SZ == dwType) {
if (!ConvertMultiSzToUnicode((LPCSTR)lpData, &pwszData)) {
DPFN(eDbgLevelError, "[SetValueExA] Multi Sz to Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
else if (REG_SZ == dwType || REG_EXPAND_SZ == dwType) {
if (!ConvertAnsiToUnicode((LPCSTR)lpData, &pwszData)) {
DPFN(eDbgLevelError, "[SetValueExA] Ansi to Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
}
if (fString) {
lRetVal = SetValueExW(
hKey,
pwszValueName,
Reserved,
dwType,
(const BYTE*)pwszData,
cbData * sizeof(WCHAR));
} else {
lRetVal = SetValueExW(
hKey,
pwszValueName,
Reserved,
dwType,
lpData,
cbData);
}
if (pwszValueName) {
MemFree(pwszValueName);
}
if (pwszData) {
MemFree(pwszData);
}
return lRetVal;
}
LONG
CLogRegistry::SetValueExW(
HKEY hKey,
LPCWSTR pwszValueName,
DWORD Reserved,
DWORD dwType,
CONST BYTE* lpData,
DWORD cbData
)
{
LONG lRetVal;
UpdateKeyList(NULL, hKey, NULL, pwszValueName, FALSE, eStartModifyValue);
lRetVal = ORIGINAL_API(RegSetValueExW)(
hKey,
pwszValueName,
Reserved,
dwType,
lpData,
cbData);
if (ERROR_SUCCESS == lRetVal) {
UpdateKeyList(NULL, hKey, NULL, pwszValueName, FALSE, eEndModifyValue);
}
return lRetVal;
}
LONG
CLogRegistry::CloseKey(
HKEY hKey
)
{
UpdateKeyList(NULL, hKey, NULL, NULL, TRUE, eRemoveKeyHandle);
return ORIGINAL_API(RegCloseKey)(hKey);
}
LONG
CLogRegistry::DeleteKeyA(
HKEY hKey,
LPCSTR pszSubKey
)
{
LPWSTR pwszSubKey = NULL;
LONG lRetVal;
//
// Stub out to DeleteKeyW.
//
if (pszSubKey) {
if (!ConvertAnsiToUnicode(pszSubKey, &pwszSubKey)) {
DPFN(eDbgLevelError, "[DeleteKeyA] Ansi -> Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
lRetVal = DeleteKeyW(hKey, pwszSubKey);
if (pwszSubKey) {
MemFree(pwszSubKey);
}
return lRetVal;
}
LONG
CLogRegistry::DeleteKeyW(
HKEY hKey,
LPCWSTR pwszSubKey
)
{
LONG lRetVal;
HKEY hKeyLocal;
//
// The caller can pass a predefined handle or an open key
// handle. In all cases, we open the key they're passing
// to force it into the list. Note that we mainly do
// this for logging purposes only.
//
hKeyLocal = ForceSubKeyIntoList(hKey, pwszSubKey);
lRetVal = ORIGINAL_API(RegDeleteKeyW)(hKey, pwszSubKey);
if (ERROR_SUCCESS == lRetVal && hKeyLocal) {
UpdateKeyList(NULL, hKeyLocal, pwszSubKey, NULL, TRUE, eDeletedKey);
}
if (hKeyLocal) {
CloseKey(hKeyLocal);
}
return lRetVal;
}
LONG
CLogRegistry::DeleteValueA(
HKEY hKey,
LPCSTR pszValueName
)
{
LPWSTR pwszValueName = NULL;
LONG lRetVal;
//
// Stub out to DeleteValueW.
//
if (pszValueName) {
if (!ConvertAnsiToUnicode(pszValueName, &pwszValueName)) {
DPFN(eDbgLevelError, "[DeleteValueA] Ansi -> Unicode failed");
return ERROR_OUTOFMEMORY;
}
}
lRetVal = DeleteValueW(hKey, pwszValueName);
if (pwszValueName) {
MemFree(pwszValueName);
}
return lRetVal;
}
LONG
CLogRegistry::DeleteValueW(
HKEY hKey,
LPCWSTR pwszValueName
)
{
LONG lRetVal;
UpdateKeyList(NULL, hKey, NULL, pwszValueName, TRUE, eStartDeleteValue);
lRetVal = ORIGINAL_API(RegDeleteValueW)(hKey, pwszValueName);
if (ERROR_SUCCESS == lRetVal) {
UpdateKeyList(NULL, hKey, NULL, pwszValueName, TRUE, eEndDeleteValue);
}
return lRetVal;
}
CLogRegistry clr;
//
// Implemenation of the actual Registry API hooks.
//
LONG
APIHOOK(RegOpenKeyA)(
HKEY hKey,
LPSTR lpSubKey,
PHKEY phkResult
)
{
CLock cLock(g_csCritSec);
return clr.OpenKeyExA(
hKey,
lpSubKey,
0,
MAXIMUM_ALLOWED,
phkResult);
}
LONG
APIHOOK(RegOpenKeyW)(
HKEY hKey,
LPWSTR lpSubKey,
PHKEY phkResult
)
{
CLock cLock(g_csCritSec);
return clr.OpenKeyExW(
hKey,
lpSubKey,
0,
MAXIMUM_ALLOWED,
phkResult);
}
LONG
APIHOOK(RegOpenKeyExA)(
HKEY hKey,
LPCSTR lpSubKey,
DWORD ulOptions,
REGSAM samDesired,
PHKEY phkResult
)
{
CLock cLock(g_csCritSec);
return clr.OpenKeyExA(
hKey,
lpSubKey,
ulOptions,
samDesired,
phkResult);
}
LONG
APIHOOK(RegOpenKeyExW)(
HKEY hKey,
LPCWSTR lpSubKey,
DWORD ulOptions,
REGSAM samDesired,
PHKEY phkResult
)
{
CLock cLock(g_csCritSec);
return clr.OpenKeyExW(
hKey,
lpSubKey,
ulOptions,
samDesired,
phkResult);
}
LONG
APIHOOK(RegOpenCurrentUser)(
REGSAM samDesired,
PHKEY phkResult
)
{
CLock cLock(g_csCritSec);
return clr.OpenCurrentUser(
samDesired,
phkResult);
}
LONG
APIHOOK(RegOpenUserClassesRoot)(
HANDLE hToken,
DWORD dwOptions,
REGSAM samDesired,
PHKEY phkResult
)
{
CLock cLock(g_csCritSec);
return clr.OpenUserClassesRoot(
hToken,
dwOptions,
samDesired,
phkResult);
}
LONG
APIHOOK(RegCreateKeyA)(
HKEY hKey,
LPCSTR lpSubKey,
PHKEY phkResult
)
{
CLock cLock(g_csCritSec);
return clr.CreateKeyExA(
hKey,
lpSubKey,
0,
NULL,
REG_OPTION_NON_VOLATILE,
MAXIMUM_ALLOWED,
NULL,
phkResult,
NULL);
}
LONG
APIHOOK(RegCreateKeyW)(
HKEY hKey,
LPCWSTR lpSubKey,
PHKEY phkResult
)
{
CLock cLock(g_csCritSec);
return clr.CreateKeyExW(
hKey,
lpSubKey,
0,
NULL,
REG_OPTION_NON_VOLATILE,
MAXIMUM_ALLOWED,
NULL,
phkResult,
NULL);
}
LONG
APIHOOK(RegCreateKeyExA)(
HKEY hKey,
LPCSTR lpSubKey,
DWORD Reserved,
LPSTR lpClass,
DWORD dwOptions,
REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
PHKEY phkResult,
LPDWORD lpdwDisposition
)
{
CLock cLock(g_csCritSec);
return clr.CreateKeyExA(
hKey,
lpSubKey,
Reserved,
lpClass,
dwOptions,
samDesired,
lpSecurityAttributes,
phkResult,
lpdwDisposition);
}
LONG
APIHOOK(RegCreateKeyExW)(
HKEY hKey,
LPCWSTR lpSubKey,
DWORD Reserved,
LPWSTR lpClass,
DWORD dwOptions,
REGSAM samDesired,
LPSECURITY_ATTRIBUTES lpSecurityAttributes,
PHKEY phkResult,
LPDWORD lpdwDisposition
)
{
CLock cLock(g_csCritSec);
return clr.CreateKeyExW(
hKey,
lpSubKey,
Reserved,
lpClass,
dwOptions,
samDesired,
lpSecurityAttributes,
phkResult,
lpdwDisposition);
}
LONG
APIHOOK(RegSetValueA)(
HKEY hKey,
LPCSTR lpSubKey,
DWORD dwType,
LPCSTR lpData,
DWORD cbData
)
{
CLock cLock(g_csCritSec);
return clr.SetValueA(
hKey,
lpSubKey,
dwType,
lpData,
cbData);
}
LONG
APIHOOK(RegSetValueW)(
HKEY hKey,
LPCWSTR lpSubKey,
DWORD dwType,
LPCWSTR lpData,
DWORD cbData
)
{
CLock cLock(g_csCritSec);
return clr.SetValueW(
hKey,
lpSubKey,
dwType,
lpData,
cbData);
}
LONG
APIHOOK(RegSetValueExA)(
HKEY hKey,
LPCSTR lpSubKey,
DWORD Reserved,
DWORD dwType,
CONST BYTE* lpData,
DWORD cbData
)
{
CLock cLock(g_csCritSec);
return clr.SetValueExA(
hKey,
lpSubKey,
Reserved,
dwType,
lpData,
cbData);
}
LONG
APIHOOK(RegSetValueExW)(
HKEY hKey,
LPCWSTR lpSubKey,
DWORD Reserved,
DWORD dwType,
CONST BYTE* lpData,
DWORD cbData
)
{
CLock cLock(g_csCritSec);
return clr.SetValueExW(
hKey,
lpSubKey,
Reserved,
dwType,
lpData,
cbData);
}
LONG
APIHOOK(RegCloseKey)(
HKEY hKey
)
{
CLock cLock(g_csCritSec);
return clr.CloseKey(
hKey);
}
LONG
APIHOOK(RegDeleteKeyA)(
HKEY hKey,
LPCSTR lpSubKey
)
{
CLock cLock(g_csCritSec);
return clr.DeleteKeyA(
hKey,
lpSubKey);
}
LONG
APIHOOK(RegDeleteKeyW)(
HKEY hKey,
LPCWSTR lpSubKey
)
{
CLock cLock(g_csCritSec);
return clr.DeleteKeyW(
hKey,
lpSubKey);
}
LONG
APIHOOK(RegDeleteValueA)(
HKEY hKey,
LPCSTR lpValueName
)
{
CLock cLock(g_csCritSec);
return clr.DeleteValueA(
hKey,
lpValueName);
}
LONG
APIHOOK(RegDeleteValueW)(
HKEY hKey,
LPCWSTR lpValueName
)
{
CLock cLock(g_csCritSec);
return clr.DeleteValueW(
hKey,
lpValueName);
}
/*++
Creates a unique id used to represent NULL values on registry calls.
--*/
void
InitializeNullValueId(
void
)
{
SYSTEMTIME st;
WCHAR* pwszSlash = NULL;
WCHAR wszModPathName[MAX_PATH];
WCHAR wszShortName[MAX_PATH];
//
// Because there is a NULL valuename for every key in the registry,
// we need a unique key that we can use to represent NULL in our list.
//
if (!GetModuleFileName(NULL, wszModPathName, ARRAYSIZE(wszModPathName))) {
StringCchCopy(wszModPathName,
ARRAYSIZE(wszModPathName),
L"uniqueexeidentifier");
}
pwszSlash = wcsrchr(wszModPathName, '\\');
if (pwszSlash) {
StringCchCopy(wszShortName, ARRAYSIZE(wszShortName), ++pwszSlash);
}
GetLocalTime(&st);
//
// The format of our unique id will look like this:
// processname.xxx-lrc-yymmdd-default
//
StringCchPrintf(g_wszUniqueId,
ARRAYSIZE(g_wszUniqueId),
L"%ls-lrc-%02hu%02hu%02hu-default",
wszShortName,
st.wYear,
st.wMonth,
st.wDay);
}
/*++
Adds the predefined key handles to the list.
--*/
BOOL
AddPredefinedHandlesToList(
void
)
{
UINT uCount;
PLOG_OPEN_KEY pKey = NULL;
HKEY rgKeys[NUM_PREDEFINED_HANDLES] = { HKEY_LOCAL_MACHINE,
HKEY_CLASSES_ROOT,
HKEY_CURRENT_USER,
HKEY_USERS,
HKEY_CURRENT_CONFIG,
HKEY_DYN_DATA,
HKEY_PERFORMANCE_DATA };
for (uCount = 0; uCount < NUM_PREDEFINED_HANDLES; uCount++) {
pKey = (PLOG_OPEN_KEY)MemAlloc(sizeof(LOG_OPEN_KEY));
if (!pKey) {
DPFN(eDbgLevelError,
"[AddPredefinedHandlesToList] No memory available");
return FALSE;
}
pKey->pwszFullKeyPath = (LPWSTR)MemAlloc(MAX_ROOT_LENGTH * sizeof(WCHAR));
if (!pKey->pwszFullKeyPath) {
DPFN(eDbgLevelError,
"[AddPredefinedHandlesToList] Failed to allocate memory");
return FALSE;
}
if (!PredefinedKeyToString(rgKeys[uCount],
MAX_ROOT_LENGTH,
&pKey->pwszFullKeyPath)) {
DPFN(eDbgLevelError,
"[AddPredefinedHandlesToList] PredefinedKey -> String failed");
return FALSE;
}
pKey->hKeyRoot = rgKeys[uCount];
pKey->hKeyBase[0] = rgKeys[uCount];
pKey->pwszSubKeyPath = NULL;
pKey->dwFlags = LRC_EXISTING_KEY;
pKey->cHandles = 1;
InitializeListHead(&pKey->KeyData);
InsertHeadList(&g_OpenKeyListHead, &pKey->Entry);
}
return TRUE;
}
/*++
Initialize the the list head and the log file.
--*/
BOOL
InitializeShim(
void
)
{
CLock cLock(g_csCritSec);
//
// Initialize our open key handle list head and the
// key data list head.
//
InitializeListHead(&g_OpenKeyListHead);
//
// Add the predefined handles to the list.
//
if (!AddPredefinedHandlesToList()) {
return FALSE;
}
InitializeNullValueId();
//
// Initialize our log file.
//
return InitializeLogFile();
}
/*++
Handle process attach notification.
--*/
BOOL
NOTIFY_FUNCTION(
DWORD fdwReason
)
{
if (fdwReason == DLL_PROCESS_ATTACH) {
return InitializeShim();
} else if (fdwReason == DLL_PROCESS_DETACH) {
return WriteListToLogFile();
}
return TRUE;
}
SHIM_INFO_BEGIN()
SHIM_INFO_DESCRIPTION(AVS_LOGREGCHANGES_DESC)
SHIM_INFO_FRIENDLY_NAME(AVS_LOGREGCHANGES_FRIENDLY)
SHIM_INFO_VERSION(1, 7)
SHIM_INFO_FLAGS(AVRF_FLAG_NO_DEFAULT | AVRF_FLAG_EXTERNAL_ONLY)
SHIM_INFO_END()
/*++
Register hooked functions.
--*/
HOOK_BEGIN
CALL_NOTIFY_FUNCTION
DUMP_VERIFIER_LOG_ENTRY(VLOG_LOGREGCHANGES_LOGLOC,
AVS_LOGREGCHANGES_LOGLOC,
AVS_LOGREGCHANGES_LOGLOC_R,
AVS_LOGREGCHANGES_LOGLOC_URL)
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyA)
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyW)
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyExA)
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenKeyExW)
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenCurrentUser)
APIHOOK_ENTRY(ADVAPI32.DLL, RegOpenUserClassesRoot)
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyA)
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyW)
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyExA)
APIHOOK_ENTRY(ADVAPI32.DLL, RegCreateKeyExW)
APIHOOK_ENTRY(ADVAPI32.DLL, RegSetValueA)
APIHOOK_ENTRY(ADVAPI32.DLL, RegSetValueW)
APIHOOK_ENTRY(ADVAPI32.DLL, RegSetValueExA)
APIHOOK_ENTRY(ADVAPI32.DLL, RegSetValueExW)
APIHOOK_ENTRY(ADVAPI32.DLL, RegDeleteKeyA)
APIHOOK_ENTRY(ADVAPI32.DLL, RegDeleteKeyW)
APIHOOK_ENTRY(ADVAPI32.DLL, RegDeleteValueA)
APIHOOK_ENTRY(ADVAPI32.DLL, RegDeleteValueW)
APIHOOK_ENTRY(ADVAPI32.DLL, RegCloseKey)
HOOK_END
IMPLEMENT_SHIM_END