3109 lines
74 KiB
C++
3109 lines
74 KiB
C++
|
/*++
|
||
|
|
||
|
Copyright (c) 2001 Microsoft Corporation
|
||
|
|
||
|
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
|
||
|
|
||
|
--*/
|
||
|
#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.
|
||
|
// This doesn't get freed.
|
||
|
//
|
||
|
LPWSTR g_pwszLogFilePath;
|
||
|
|
||
|
//
|
||
|
// 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;
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Writes an entry to the log file.
|
||
|
|
||
|
--*/
|
||
|
void
|
||
|
WriteEntryToLog(
|
||
|
IN LPCWSTR pwszEntry
|
||
|
)
|
||
|
{
|
||
|
int nLen = 0;
|
||
|
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] Failed to open log");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Write the data out to the file.
|
||
|
//
|
||
|
nLen = wcslen(pwszEntry);
|
||
|
|
||
|
IoStatusBlock.Status = 0;
|
||
|
IoStatusBlock.Information = 0;
|
||
|
|
||
|
liOffset.LowPart = 0;
|
||
|
liOffset.HighPart = 0;
|
||
|
|
||
|
status = NtWriteFile(hFile,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&IoStatusBlock,
|
||
|
(PVOID)pwszEntry,
|
||
|
(ULONG)(nLen) * sizeof(WCHAR),
|
||
|
&liOffset,
|
||
|
NULL);
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
DPFN(eDbgLevelError, "[WriteEntryToLog] 0x%X Failed to make entry", status);
|
||
|
goto exit;
|
||
|
}
|
||
|
|
||
|
exit:
|
||
|
|
||
|
NtClose(hFile);
|
||
|
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Creates our log files using the local time.
|
||
|
It goes into %windir%\AppPatch\VLog.
|
||
|
|
||
|
--*/
|
||
|
BOOL
|
||
|
InitializeLogFile(
|
||
|
void
|
||
|
)
|
||
|
{
|
||
|
BOOL fReturn = FALSE;
|
||
|
HANDLE hFile;
|
||
|
SYSTEMTIME st;
|
||
|
UINT nLen = 0;
|
||
|
WCHAR wszFileLog[64];
|
||
|
WCHAR* pwszLogFile = NULL;
|
||
|
WCHAR* pSlash = NULL;
|
||
|
WCHAR* pDot = NULL;
|
||
|
WCHAR wszModPathName[MAX_PATH];
|
||
|
WCHAR wszShortName[MAX_PATH];
|
||
|
WCHAR wszLogHdr[512];
|
||
|
const WCHAR wszLogDir[] = L"\\AppPatch\\VLog";
|
||
|
WCHAR wszUnicodeHdr = 0xFEFF;
|
||
|
UNICODE_STRING strLogFile;
|
||
|
NTSTATUS status;
|
||
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
|
IO_STATUS_BLOCK IoStatusBlock;
|
||
|
|
||
|
//
|
||
|
// Format the log header.
|
||
|
//
|
||
|
if (!GetModuleFileName(NULL, wszModPathName, ARRAYSIZE(wszModPathName))) {
|
||
|
wcscpy(wszModPathName, L"unknown");
|
||
|
}
|
||
|
|
||
|
if (_snwprintf(wszLogHdr,
|
||
|
ARRAYSIZE(wszLogHdr) - 1,
|
||
|
L"%lc<?xml version=\"1.0\" encoding=\"UTF-16\"?>\r\n<APPLICATION NAME=\"%ls\">\r\n",
|
||
|
wszUnicodeHdr,
|
||
|
wszModPathName) < 0) {
|
||
|
DPFN(eDbgLevelError, "[InitializeLogFile] Buffer too small (1)");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Get the current time and set up the log file name.
|
||
|
// The format of the log file name is this:
|
||
|
// 'processname_registry_yyyymmdd_hhmmss.xml'
|
||
|
//
|
||
|
GetLocalTime(&st);
|
||
|
|
||
|
//
|
||
|
// Extract the name of this module without the path and extension.
|
||
|
// Basically we trim off the extension first (if there is one.)
|
||
|
// Next we find out where the directory path ends, then grab
|
||
|
// the file portion and save it away.
|
||
|
//
|
||
|
wszShortName[0] = 0;
|
||
|
pDot = wcsrchr(wszModPathName, '.');
|
||
|
|
||
|
if (pDot) {
|
||
|
*pDot = '\0';
|
||
|
}
|
||
|
|
||
|
pSlash = wcsrchr(wszModPathName, '\\');
|
||
|
|
||
|
if (pSlash) {
|
||
|
wcsncpy(wszShortName, ++pSlash, (wcslen(pSlash) + 1));
|
||
|
}
|
||
|
|
||
|
if (_snwprintf(wszFileLog,
|
||
|
ARRAYSIZE(wszFileLog) - 1,
|
||
|
L"%ls_registry_%02hu%02hu%02hu_%02hu%02hu%02hu.xml",
|
||
|
wszShortName,
|
||
|
st.wYear,
|
||
|
st.wMonth,
|
||
|
st.wDay,
|
||
|
st.wHour,
|
||
|
st.wMinute,
|
||
|
st.wSecond) < 0) {
|
||
|
DPFN(eDbgLevelError, "[InitializeLogFile] Buffer too small (2)");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Set up the path our log file.
|
||
|
//
|
||
|
nLen = GetSystemWindowsDirectory(NULL, 0);
|
||
|
|
||
|
if (0 == nLen) {
|
||
|
DPFN(eDbgLevelError,
|
||
|
"[InitializeLogFile] %lu Failed to get size for Windows directory path",
|
||
|
GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
nLen += wcslen(wszFileLog);
|
||
|
nLen += wcslen(wszLogDir);
|
||
|
|
||
|
pwszLogFile = (LPWSTR)MemAlloc((nLen + 2) * sizeof(WCHAR));
|
||
|
|
||
|
if (!pwszLogFile) {
|
||
|
DPFN(eDbgLevelError, "[InitializeLogFile] No memory for log file");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
nLen = GetSystemWindowsDirectory(pwszLogFile, (nLen + 2) * sizeof(WCHAR));
|
||
|
|
||
|
if (0 == nLen) {
|
||
|
DPFN(eDbgLevelError,
|
||
|
"[InitializeLogFile] %lu Failed to get Windows directory path",
|
||
|
GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
wcscat(pwszLogFile, wszLogDir);
|
||
|
|
||
|
//
|
||
|
// Ensure that the %windir%\AppPatch\VLog directory exists.
|
||
|
// If it doesn't, attempt to create it.
|
||
|
//
|
||
|
if (-1 == GetFileAttributes(pwszLogFile)) {
|
||
|
if (!CreateDirectory(pwszLogFile, NULL)) {
|
||
|
DPFN(eDbgLevelError,
|
||
|
"[InitializeLogFile] %lu Failed to create VLog directory",
|
||
|
GetLastError());
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
wcscat(pwszLogFile, L"\\");
|
||
|
wcscat(pwszLogFile, wszFileLog);
|
||
|
|
||
|
//
|
||
|
// Save the pointer as we'll need it later.
|
||
|
//
|
||
|
g_pwszLogFilePath = pwszLogFile;
|
||
|
|
||
|
//
|
||
|
// Attempt to create the new log file.
|
||
|
//
|
||
|
RtlInitUnicodeString(&strLogFile, pwszLogFile);
|
||
|
|
||
|
status = RtlDosPathNameToNtPathName_U(strLogFile.Buffer,
|
||
|
&strLogFile,
|
||
|
NULL,
|
||
|
NULL);
|
||
|
|
||
|
if (!NT_SUCCESS(status)) {
|
||
|
DPFN(eDbgLevelError, "[InitializeLogFile] DOS -> NT failed");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
|
||
|
//
|
||
|
// Save away the NT path to the file.
|
||
|
//
|
||
|
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] Failed to save log file path");
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Write the header to the log.
|
||
|
//
|
||
|
WriteEntryToLog(wszLogHdr);
|
||
|
|
||
|
fReturn = TRUE;
|
||
|
|
||
|
cleanup:
|
||
|
|
||
|
RtlFreeUnicodeString(&strLogFile);
|
||
|
|
||
|
return fReturn;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
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_pwszLogFilePath);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Converts from ANSI to Unicode. The caller must free the buffer.
|
||
|
|
||
|
--*/
|
||
|
BOOL
|
||
|
ConvertAnsiToUnicode(
|
||
|
IN LPCSTR pszAnsiString,
|
||
|
OUT LPWSTR* pwszUnicodeString
|
||
|
)
|
||
|
{
|
||
|
int nLen = 0;
|
||
|
|
||
|
nLen = lstrlenA(pszAnsiString);
|
||
|
|
||
|
if (nLen) {
|
||
|
*pwszUnicodeString = (LPWSTR)MemAlloc((nLen + 1) * sizeof(WCHAR));
|
||
|
|
||
|
if (!*pwszUnicodeString) {
|
||
|
DPFN(eDbgLevelError,
|
||
|
"[ConvertAnsiToUnicode] Failed to allocate memory");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
nLen = MultiByteToWideChar(CP_ACP,
|
||
|
0,
|
||
|
pszAnsiString,
|
||
|
-1,
|
||
|
*pwszUnicodeString,
|
||
|
nLen + 1);
|
||
|
|
||
|
if (!nLen) {
|
||
|
DPFN(eDbgLevelError,
|
||
|
"[ConvertAnsiToUnicode] %lu 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 uSize = 0;
|
||
|
UINT uWideSize = 0;
|
||
|
UINT uTotalSize = 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 {
|
||
|
uSize = lstrlenA(pszAnsi) + 1;
|
||
|
uTotalSize += uSize;
|
||
|
pszAnsi += uSize;
|
||
|
} while (uSize != 1);
|
||
|
|
||
|
if (uTotalSize != 0) {
|
||
|
pwszTemp = *pwszWideStringList = (LPWSTR)MemAlloc(uTotalSize * 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;
|
||
|
|
||
|
uWideSize = MultiByteToWideChar(
|
||
|
CP_ACP,
|
||
|
0,
|
||
|
pszAnsi,
|
||
|
-1,
|
||
|
pwszTemp,
|
||
|
nLen);
|
||
|
|
||
|
pszAnsi += nLen;
|
||
|
pwszTemp += uWideSize;
|
||
|
} while (nLen != 1);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Given a predefined key handle such as HKEY_LOCAL_MACHINE, return a string.
|
||
|
|
||
|
--*/
|
||
|
BOOL
|
||
|
PredefinedKeyToString(
|
||
|
IN HKEY hKey,
|
||
|
OUT LPWSTR* pwszString
|
||
|
)
|
||
|
{
|
||
|
if (hKey == HKEY_CLASSES_ROOT) {
|
||
|
wcscpy(*pwszString, L"HKEY_CLASSES_ROOT");
|
||
|
}
|
||
|
else if (hKey == HKEY_CURRENT_CONFIG) {
|
||
|
wcscpy(*pwszString, L"HKEY_CURRENT_CONFIG");
|
||
|
}
|
||
|
else if (hKey == HKEY_CURRENT_USER) {
|
||
|
wcscpy(*pwszString, L"HKEY_CURRENT_USER");
|
||
|
}
|
||
|
else if (hKey == HKEY_LOCAL_MACHINE) {
|
||
|
wcscpy(*pwszString, L"HKEY_LOCAL_MACHINE");
|
||
|
}
|
||
|
else if (hKey == HKEY_USERS) {
|
||
|
wcscpy(*pwszString, L"HKEY_USERS");
|
||
|
}
|
||
|
else if (hKey == HKEY_DYN_DATA) {
|
||
|
wcscpy(*pwszString, L"HKEY_DYN_DATA");
|
||
|
}
|
||
|
else if (hKey == HKEY_PERFORMANCE_DATA) {
|
||
|
wcscpy(*pwszString, L"HKEY_PERFORMANCE_DATA");
|
||
|
}
|
||
|
else {
|
||
|
wcscpy(*pwszString, 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] = 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 dwLen = 0;
|
||
|
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) {
|
||
|
dwLen = wcslen(pwszSubKeyPath);
|
||
|
}
|
||
|
|
||
|
if (pFindKey->pwszSubKeyPath) {
|
||
|
dwLen += wcslen(pFindKey->pwszSubKeyPath);
|
||
|
}
|
||
|
|
||
|
if (pFindKey->pwszSubKeyPath || pwszSubKeyPath) {
|
||
|
pRetKey->pwszSubKeyPath = (LPWSTR)MemAlloc((dwLen + 2) * 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) {
|
||
|
wcscpy(pRetKey->pwszSubKeyPath, pFindKey->pwszSubKeyPath);
|
||
|
wcscat(pRetKey->pwszSubKeyPath, L"\\");
|
||
|
wcscat(pRetKey->pwszSubKeyPath, pwszSubKeyPath);
|
||
|
} else if (pwszSubKeyPath) {
|
||
|
wcscpy(pRetKey->pwszSubKeyPath, 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) {
|
||
|
dwLen = wcslen(pRetKey->pwszSubKeyPath);
|
||
|
}
|
||
|
|
||
|
pRetKey->pwszFullKeyPath = (LPWSTR)MemAlloc((dwLen + 2 + MAX_ROOT_LENGTH) * 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, &pRetKey->pwszFullKeyPath)) {
|
||
|
DPFN(eDbgLevelError,
|
||
|
"[AddKeyHandleToList] PredefinedKey -> String failed");
|
||
|
goto cleanup;
|
||
|
}
|
||
|
|
||
|
if (pwszSubKeyPath) {
|
||
|
wcscat(pRetKey->pwszFullKeyPath, L"\\");
|
||
|
wcscat(pRetKey->pwszFullKeyPath, 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) {
|
||
|
wcsncpy(pKeyData->wszValueName, pwszValueName, MAX_PATH);
|
||
|
} else {
|
||
|
//
|
||
|
// If the valuename is NULL, assign our unique id.
|
||
|
//
|
||
|
wcsncpy(pKeyData->wszValueName, g_wszUniqueId, wcslen(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 = 0;
|
||
|
PVOID pTemp = NULL;
|
||
|
|
||
|
//
|
||
|
// 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"&");
|
||
|
csFullKeyPath.Replace(L"<", L"<");
|
||
|
csFullKeyPath.Replace(L">", L">");
|
||
|
csFullKeyPath.Replace(L"'", L"'");
|
||
|
csFullKeyPath.Replace(L"\"", L""");
|
||
|
|
||
|
//
|
||
|
// 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, "[FormatKeyData] Failed to allocate memory");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
g_cbTempBufferSize = TEMP_BUFFER_SIZE * sizeof(WCHAR);
|
||
|
}
|
||
|
|
||
|
g_pwszTempBuffer[0] = 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,
|
||
|
"[FormatKeyData] Failed to reallocate memory");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
g_cbTempBufferSize = cbSize + BUFFER_ALLOCATION_DELTA;
|
||
|
}
|
||
|
|
||
|
wsprintf(g_pwszTempBuffer,
|
||
|
L" <OPERATION TYPE=\"%ls\" KEY_PATH=\"%ls\"/>\r\n",
|
||
|
pwszOperation,
|
||
|
csFullKeyPath.Get());
|
||
|
|
||
|
WriteEntryToLog(g_pwszTempBuffer);
|
||
|
}
|
||
|
|
||
|
void
|
||
|
FormatValueDataIntoElement(
|
||
|
IN CString& csFullKeyPath,
|
||
|
IN LPCWSTR pwszOperation,
|
||
|
IN LPCWSTR pwszValueName,
|
||
|
IN LPCWSTR pwszOriginalValueType,
|
||
|
IN LPCWSTR pwszFinalValueType
|
||
|
)
|
||
|
{
|
||
|
UINT cbSize = 0;
|
||
|
int nChars = 0;
|
||
|
PVOID pTemp = NULL;
|
||
|
|
||
|
//
|
||
|
// 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.
|
||
|
//
|
||
|
nChars = wsprintf(g_pwszTempBuffer, L" <OPERATION TYPE=\"%ls\" KEY_PATH=\"%ls\">\r\n",
|
||
|
pwszOperation,
|
||
|
csFullKeyPath.Get());
|
||
|
|
||
|
//
|
||
|
// Write the <VALUE_NAME> element.
|
||
|
//
|
||
|
if (pwszValueName) {
|
||
|
nChars += wsprintf(g_pwszTempBuffer + nChars, L" <VALUE_NAME><![CDATA[%ls]]></VALUE_NAME>\r\n",
|
||
|
pwszValueName);
|
||
|
} else {
|
||
|
nChars += wsprintf(g_pwszTempBuffer + nChars, L" <VALUE_NAME/>\r\n");
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Write the <ORIGINAL_DATA> element.
|
||
|
//
|
||
|
if (g_pwszOriginalData[0]) {
|
||
|
nChars += wsprintf(g_pwszTempBuffer + nChars, L" <ORIGINAL_DATA TYPE=\"%ls\"><![CDATA[%ls]]></ORIGINAL_DATA>\r\n",
|
||
|
pwszOriginalValueType,
|
||
|
g_pwszOriginalData);
|
||
|
} else {
|
||
|
nChars += wsprintf(g_pwszTempBuffer + nChars, L" <ORIGINAL_DATA/>\r\n");
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Write the <FINAL_DATA> element.
|
||
|
//
|
||
|
if (g_pwszFinalData[0]) {
|
||
|
nChars += wsprintf(g_pwszTempBuffer + nChars, L" <FINAL_DATA TYPE=\"%ls\"><![CDATA[%ls]]></FINAL_DATA>\r\n",
|
||
|
pwszFinalValueType,
|
||
|
g_pwszFinalData);
|
||
|
} else {
|
||
|
nChars += wsprintf(g_pwszTempBuffer + nChars, L" <FINAL_DATA/>\r\n");
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Close the <OPERATION> element.
|
||
|
//
|
||
|
wsprintf(g_pwszTempBuffer + nChars, L" </OPERATION>\r\n");
|
||
|
|
||
|
WriteEntryToLog(g_pwszTempBuffer);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Converts binary data into a readable string.
|
||
|
|
||
|
--*/
|
||
|
void
|
||
|
ExtractBinaryData(
|
||
|
IN PVOID pBinary,
|
||
|
IN DWORD dwDataSize,
|
||
|
IN OUT LPWSTR* pwszString
|
||
|
)
|
||
|
{
|
||
|
int nChars = 0;
|
||
|
PBYTE pByte = NULL;
|
||
|
DWORD dwLoop = 0;
|
||
|
|
||
|
if (!pBinary || !pwszString) {
|
||
|
DPFN(eDbgLevelError, "[ExtractBinaryData] Invalid parameter(s)");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
pByte = (BYTE*)pBinary;
|
||
|
dwLoop = dwDataSize / sizeof(WCHAR);
|
||
|
|
||
|
while (dwLoop) {
|
||
|
nChars += wsprintf(*pwszString + nChars, L"%lx", *pByte++);
|
||
|
dwLoop--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*++
|
||
|
|
||
|
Converts a REG_MULTI_SZ to a readable string.
|
||
|
|
||
|
--*/
|
||
|
void
|
||
|
ExtractMultiSzStrings(
|
||
|
IN PVOID pMultiSz,
|
||
|
IN OUT LPWSTR* pwszString
|
||
|
)
|
||
|
{
|
||
|
int nChars = 0;
|
||
|
UINT uSize = 0;
|
||
|
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;
|
||
|
|
||
|
while (TRUE) {
|
||
|
nChars += wsprintf(*pwszString + nChars, 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] = 0;
|
||
|
g_pwszFinalData[0] = 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"&");
|
||
|
csFullKeyPath.Replace(L"<", L"<");
|
||
|
csFullKeyPath.Replace(L">", L">");
|
||
|
csFullKeyPath.Replace(L"'", L"'");
|
||
|
csFullKeyPath.Replace(L"\"", L""");
|
||
|
|
||
|
switch (pKeyData->dwOriginalValueType) {
|
||
|
case REG_SZ:
|
||
|
wcscpy(wszOriginalValueType, L"REG_SZ");
|
||
|
break;
|
||
|
case REG_EXPAND_SZ:
|
||
|
wcscpy(wszOriginalValueType, L"REG_EXPAND_SZ");
|
||
|
break;
|
||
|
case REG_MULTI_SZ:
|
||
|
wcscpy(wszOriginalValueType, L"REG_MULTI_SZ");
|
||
|
break;
|
||
|
case REG_DWORD:
|
||
|
wcscpy(wszOriginalValueType, L"REG_DWORD");
|
||
|
break;
|
||
|
case REG_BINARY:
|
||
|
wcscpy(wszOriginalValueType, L"REG_BINARY");
|
||
|
break;
|
||
|
default:
|
||
|
wcscpy(wszOriginalValueType, L"Unknown");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
switch (pKeyData->dwFinalValueType) {
|
||
|
case REG_SZ:
|
||
|
wcscpy(wszFinalValueType, L"REG_SZ");
|
||
|
break;
|
||
|
case REG_EXPAND_SZ:
|
||
|
wcscpy(wszFinalValueType, L"REG_EXPAND_SZ");
|
||
|
break;
|
||
|
case REG_MULTI_SZ:
|
||
|
wcscpy(wszFinalValueType, L"REG_MULTI_SZ");
|
||
|
break;
|
||
|
case REG_DWORD:
|
||
|
wcscpy(wszFinalValueType, L"REG_DWORD");
|
||
|
break;
|
||
|
case REG_BINARY:
|
||
|
wcscpy(wszFinalValueType, L"REG_BINARY");
|
||
|
break;
|
||
|
default:
|
||
|
wcscpy(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:
|
||
|
wsprintf(g_pwszOriginalData, L"%lu", (*(DWORD*)pKeyData->pOriginalData));
|
||
|
break;
|
||
|
|
||
|
case REG_SZ:
|
||
|
case REG_EXPAND_SZ:
|
||
|
wcscpy(g_pwszOriginalData, (const WCHAR*)pKeyData->pOriginalData);
|
||
|
break;
|
||
|
|
||
|
case REG_MULTI_SZ:
|
||
|
ExtractMultiSzStrings(pKeyData->pOriginalData,
|
||
|
&g_pwszOriginalData);
|
||
|
break;
|
||
|
|
||
|
case REG_BINARY:
|
||
|
ExtractBinaryData(pKeyData->pOriginalData,
|
||
|
pKeyData->cbOriginalDataSize,
|
||
|
&g_pwszOriginalData);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
DPFN(eDbgLevelError, "[FormatValueData] Unsupported value type");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (pKeyData->pFinalData) {
|
||
|
switch (pKeyData->dwFinalValueType) {
|
||
|
case REG_DWORD:
|
||
|
wsprintf(g_pwszFinalData, L"%lu", (*(DWORD*)pKeyData->pFinalData));
|
||
|
break;
|
||
|
|
||
|
case REG_SZ:
|
||
|
case REG_EXPAND_SZ:
|
||
|
wcscpy(g_pwszFinalData, (const WCHAR*)pKeyData->pFinalData);
|
||
|
break;
|
||
|
|
||
|
case REG_MULTI_SZ:
|
||
|
ExtractMultiSzStrings(pKeyData->pFinalData,
|
||
|
&g_pwszFinalData);
|
||
|
break;
|
||
|
|
||
|
case REG_BINARY:
|
||
|
ExtractBinaryData(pKeyData->pFinalData,
|
||
|
pKeyData->cbFinalDataSize,
|
||
|
&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;
|
||
|
|
||
|
return clr.OpenKeyExA(
|
||
|
hKey,
|
||
|
lpSubKey,
|
||
|
0,
|
||
|
MAXIMUM_ALLOWED,
|
||
|
phkResult);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegOpenKeyW)(
|
||
|
HKEY hKey,
|
||
|
LPWSTR lpSubKey,
|
||
|
PHKEY phkResult
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
return clr.OpenKeyExW(
|
||
|
hKey,
|
||
|
lpSubKey,
|
||
|
0,
|
||
|
MAXIMUM_ALLOWED,
|
||
|
phkResult);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegOpenKeyExA)(
|
||
|
HKEY hKey,
|
||
|
LPCSTR lpSubKey,
|
||
|
DWORD ulOptions,
|
||
|
REGSAM samDesired,
|
||
|
PHKEY phkResult
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
return clr.OpenKeyExA(
|
||
|
hKey,
|
||
|
lpSubKey,
|
||
|
ulOptions,
|
||
|
samDesired,
|
||
|
phkResult);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegOpenKeyExW)(
|
||
|
HKEY hKey,
|
||
|
LPCWSTR lpSubKey,
|
||
|
DWORD ulOptions,
|
||
|
REGSAM samDesired,
|
||
|
PHKEY phkResult
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
return clr.OpenKeyExW(
|
||
|
hKey,
|
||
|
lpSubKey,
|
||
|
ulOptions,
|
||
|
samDesired,
|
||
|
phkResult);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegOpenCurrentUser)(
|
||
|
REGSAM samDesired,
|
||
|
PHKEY phkResult
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
return clr.OpenCurrentUser(
|
||
|
samDesired,
|
||
|
phkResult);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegOpenUserClassesRoot)(
|
||
|
HANDLE hToken,
|
||
|
DWORD dwOptions,
|
||
|
REGSAM samDesired,
|
||
|
PHKEY phkResult
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
return clr.OpenUserClassesRoot(
|
||
|
hToken,
|
||
|
dwOptions,
|
||
|
samDesired,
|
||
|
phkResult);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegCreateKeyA)(
|
||
|
HKEY hKey,
|
||
|
LPCSTR lpSubKey,
|
||
|
PHKEY phkResult
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
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;
|
||
|
|
||
|
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;
|
||
|
|
||
|
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;
|
||
|
|
||
|
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;
|
||
|
|
||
|
return clr.SetValueA(
|
||
|
hKey,
|
||
|
lpSubKey,
|
||
|
dwType,
|
||
|
lpData,
|
||
|
cbData);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegSetValueW)(
|
||
|
HKEY hKey,
|
||
|
LPCWSTR lpSubKey,
|
||
|
DWORD dwType,
|
||
|
LPCWSTR lpData,
|
||
|
DWORD cbData
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
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;
|
||
|
|
||
|
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;
|
||
|
|
||
|
return clr.SetValueExW(
|
||
|
hKey,
|
||
|
lpSubKey,
|
||
|
Reserved,
|
||
|
dwType,
|
||
|
lpData,
|
||
|
cbData);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegCloseKey)(
|
||
|
HKEY hKey
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
return clr.CloseKey(
|
||
|
hKey);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegDeleteKeyA)(
|
||
|
HKEY hKey,
|
||
|
LPCSTR lpSubKey
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
return clr.DeleteKeyA(
|
||
|
hKey,
|
||
|
lpSubKey);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegDeleteKeyW)(
|
||
|
HKEY hKey,
|
||
|
LPCWSTR lpSubKey
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
return clr.DeleteKeyW(
|
||
|
hKey,
|
||
|
lpSubKey);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegDeleteValueA)(
|
||
|
HKEY hKey,
|
||
|
LPCSTR lpValueName
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
return clr.DeleteValueA(
|
||
|
hKey,
|
||
|
lpValueName);
|
||
|
}
|
||
|
|
||
|
LONG
|
||
|
APIHOOK(RegDeleteValueW)(
|
||
|
HKEY hKey,
|
||
|
LPCWSTR lpValueName
|
||
|
)
|
||
|
{
|
||
|
CLock cLock;
|
||
|
|
||
|
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))) {
|
||
|
wcscpy(wszModPathName, L"uniqueexeidentifier");
|
||
|
}
|
||
|
|
||
|
pwszSlash = wcsrchr(wszModPathName, '\\');
|
||
|
|
||
|
if (pwszSlash) {
|
||
|
wcsncpy(wszShortName, ++pwszSlash, (wcslen(pwszSlash)+1));
|
||
|
}
|
||
|
|
||
|
GetLocalTime(&st);
|
||
|
|
||
|
//
|
||
|
// The format of our unique id will look like this:
|
||
|
// processname.xxx-lrc-yymmdd-default
|
||
|
//
|
||
|
wsprintf(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], &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;
|
||
|
|
||
|
//
|
||
|
// 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, 5)
|
||
|
SHIM_INFO_FLAGS(AVRF_FLAG_NO_DEFAULT)
|
||
|
|
||
|
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
|