forked from TeamTofuShop/segatools
		
	
		
			
				
	
	
		
			798 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			798 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <windows.h>
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <stdbool.h>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #include "hook/table.h"
 | |
| 
 | |
| #include "hooklib/reg.h"
 | |
| 
 | |
| #include "util/dprintf.h"
 | |
| #include "util/str.h"
 | |
| 
 | |
| struct reg_hook_key {
 | |
|     HKEY root;
 | |
|     const wchar_t *name;
 | |
|     const struct reg_hook_val *vals;
 | |
|     size_t nvals;
 | |
|     HKEY handle;
 | |
| };
 | |
| 
 | |
| /* Helper functions */
 | |
| 
 | |
| static void reg_hook_init(void);
 | |
| 
 | |
| static LRESULT reg_hook_propagate_hr(HRESULT hr);
 | |
| 
 | |
| static struct reg_hook_key *reg_hook_match_key_locked(HKEY handle);
 | |
| 
 | |
| static const struct reg_hook_val *reg_hook_match_val_locked(
 | |
|         struct reg_hook_key *key,
 | |
|         const wchar_t *name);
 | |
| 
 | |
| static LSTATUS reg_hook_open_locked(
 | |
|         HKEY parent,
 | |
|         const wchar_t *name,
 | |
|         HKEY *out);
 | |
| 
 | |
| static LSTATUS reg_hook_query_val_locked(
 | |
|         struct reg_hook_key *key,
 | |
|         const wchar_t *name,
 | |
|         uint32_t *type,
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes);
 | |
| 
 | |
| /* API hooks */
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegOpenKeyExW(
 | |
|         HKEY parent,
 | |
|         const wchar_t *name,
 | |
|         uint32_t flags,
 | |
|         uint32_t access,
 | |
|         HKEY *out);
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegCreateKeyExW(
 | |
|         HKEY parent,
 | |
|         const wchar_t *name,
 | |
|         uint32_t reserved,
 | |
|         const wchar_t *class_,
 | |
|         uint32_t options,
 | |
|         uint32_t access,
 | |
|         const SECURITY_ATTRIBUTES *sa,
 | |
|         HKEY *out,
 | |
|         uint32_t *disposition);
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegCloseKey(HKEY handle);
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegQueryValueExA(
 | |
|         HKEY handle,
 | |
|         const char *name,
 | |
|         void *reserved,
 | |
|         uint32_t *type,
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes);
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegQueryValueExW(
 | |
|         HKEY handle,
 | |
|         const wchar_t *name,
 | |
|         void *reserved,
 | |
|         uint32_t *type,
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes);
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegSetValueExW(
 | |
|         HKEY handle,
 | |
|         const wchar_t *name,
 | |
|         uint32_t reserved,
 | |
|         uint32_t type,
 | |
|         const void *bytes,
 | |
|         uint32_t nbytes);
 | |
| 
 | |
| /* Link pointers */
 | |
| 
 | |
| static LSTATUS (WINAPI *next_RegOpenKeyExW)(
 | |
|         HKEY parent,
 | |
|         const wchar_t *name,
 | |
|         uint32_t flags,
 | |
|         uint32_t access,
 | |
|         HKEY *out);
 | |
| 
 | |
| static LSTATUS (WINAPI *next_RegCreateKeyExW)(
 | |
|         HKEY parent,
 | |
|         const wchar_t *name,
 | |
|         uint32_t reserved,
 | |
|         const wchar_t *class_,
 | |
|         uint32_t options,
 | |
|         uint32_t access,
 | |
|         const SECURITY_ATTRIBUTES *sa,
 | |
|         HKEY *out,
 | |
|         uint32_t *disposition);
 | |
| 
 | |
| static LSTATUS (WINAPI *next_RegCloseKey)(HKEY handle);
 | |
| 
 | |
| static LSTATUS (WINAPI *next_RegQueryValueExA)(
 | |
|         HKEY handle,
 | |
|         const char *name,
 | |
|         void *reserved,
 | |
|         uint32_t *type,
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes);
 | |
| 
 | |
| static LSTATUS (WINAPI *next_RegQueryValueExW)(
 | |
|         HKEY handle,
 | |
|         const wchar_t *name,
 | |
|         void *reserved,
 | |
|         uint32_t *type,
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes);
 | |
| 
 | |
| static LSTATUS (WINAPI *next_RegSetValueExW)(
 | |
|         HKEY handle,
 | |
|         const wchar_t *name,
 | |
|         uint32_t reserved,
 | |
|         uint32_t type,
 | |
|         const void *bytes,
 | |
|         uint32_t nbytes);
 | |
| 
 | |
| static const struct hook_symbol reg_hook_syms[] = {
 | |
|     {
 | |
|         .name   = "RegOpenKeyExW",
 | |
|         .patch  = hook_RegOpenKeyExW,
 | |
|         .link   = (void **) &next_RegOpenKeyExW,
 | |
|     }, {
 | |
|         .name   = "RegCreateKeyExW",
 | |
|         .patch  = hook_RegCreateKeyExW,
 | |
|         .link   = (void **) &next_RegCreateKeyExW,
 | |
|     }, {
 | |
|         .name   = "RegCloseKey",
 | |
|         .patch  = hook_RegCloseKey,
 | |
|         .link   = (void **) &next_RegCloseKey,
 | |
|     }, {
 | |
|         .name   = "RegQueryValueExA",
 | |
|         .patch  = hook_RegQueryValueExA,
 | |
|         .link   = (void **) &next_RegQueryValueExA,
 | |
|     }, {
 | |
|         .name   = "RegQueryValueExW",
 | |
|         .patch  = hook_RegQueryValueExW,
 | |
|         .link   = (void **) &next_RegQueryValueExW,
 | |
|     }, {
 | |
|         .name   = "RegSetValueExW",
 | |
|         .patch  = hook_RegSetValueExW,
 | |
|         .link   = (void **) &next_RegSetValueExW,
 | |
|     }
 | |
| };
 | |
| 
 | |
| static bool reg_hook_initted;
 | |
| static CRITICAL_SECTION reg_hook_lock;
 | |
| static struct reg_hook_key *reg_hook_keys;
 | |
| static size_t reg_hook_nkeys;
 | |
| 
 | |
| HRESULT reg_hook_push_key(
 | |
|         HKEY root,
 | |
|         const wchar_t *name,
 | |
|         const struct reg_hook_val *vals,
 | |
|         size_t nvals)
 | |
| {
 | |
|     struct reg_hook_key *new_mem;
 | |
|     struct reg_hook_key *new_key;
 | |
|     HRESULT hr;
 | |
| 
 | |
|     assert(root != NULL);
 | |
|     assert(name != NULL);
 | |
|     assert(vals != NULL || nvals == 0);
 | |
| 
 | |
|     reg_hook_init();
 | |
| 
 | |
|     EnterCriticalSection(®_hook_lock);
 | |
| 
 | |
|     new_mem = realloc(
 | |
|             reg_hook_keys,
 | |
|             (reg_hook_nkeys + 1) * sizeof(struct reg_hook_key));
 | |
| 
 | |
|     if (new_mem == NULL) {
 | |
|         hr = E_OUTOFMEMORY;
 | |
| 
 | |
|         goto end;
 | |
|     }
 | |
| 
 | |
|     new_key = &new_mem[reg_hook_nkeys];
 | |
|     memset(new_key, 0, sizeof(*new_key));
 | |
|     new_key->root = root;
 | |
|     new_key->name = name; /* Expect this to be statically allocated */
 | |
|     new_key->vals = vals;
 | |
|     new_key->nvals = nvals;
 | |
| 
 | |
|     reg_hook_keys = new_mem;
 | |
|     reg_hook_nkeys++;
 | |
| 
 | |
|     hr = S_OK;
 | |
| 
 | |
| end:
 | |
|     LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|     return hr;
 | |
| }
 | |
| 
 | |
| static void reg_hook_init(void)
 | |
| {
 | |
|     if (reg_hook_initted) {
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     reg_hook_initted = true;
 | |
|     InitializeCriticalSection(®_hook_lock);
 | |
| 
 | |
|     hook_table_apply(
 | |
|             NULL,
 | |
|             "advapi32.dll",
 | |
|             reg_hook_syms,
 | |
|             _countof(reg_hook_syms));
 | |
| }
 | |
| 
 | |
| static LRESULT reg_hook_propagate_hr(HRESULT hr)
 | |
| {
 | |
|     if (SUCCEEDED(hr)) {
 | |
|         return ERROR_SUCCESS;
 | |
|     } else if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
 | |
|         return HRESULT_CODE(hr);
 | |
|     } else {
 | |
|         return ERROR_GEN_FAILURE;
 | |
|     }
 | |
| }
 | |
| 
 | |
| static struct reg_hook_key *reg_hook_match_key_locked(HKEY handle)
 | |
| {
 | |
|     struct reg_hook_key *key;
 | |
|     size_t i;
 | |
| 
 | |
|     if (handle == NULL || handle == INVALID_HANDLE_VALUE) {
 | |
|         return NULL;
 | |
|     }
 | |
| 
 | |
|     for (i = 0 ; i < reg_hook_nkeys ; i++) {
 | |
|         key = ®_hook_keys[i];
 | |
| 
 | |
|         if (key->handle == handle) {
 | |
|             return key;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static const struct reg_hook_val *reg_hook_match_val_locked(
 | |
|         struct reg_hook_key *key,
 | |
|         const wchar_t *name)
 | |
| {
 | |
|     const struct reg_hook_val *val;
 | |
|     size_t i;
 | |
| 
 | |
|     /* Watch out for accesses to the key's default value */
 | |
| 
 | |
|     if (name == NULL) {
 | |
|         name = L"";
 | |
|     }
 | |
| 
 | |
|     for (i = 0 ; i < key->nvals ; i++) {
 | |
|         val = &key->vals[i];
 | |
| 
 | |
|         if (wstr_ieq(val->name, name)) {
 | |
|             return val;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| static LSTATUS reg_hook_open_locked(
 | |
|         HKEY parent,
 | |
|         const wchar_t *name,
 | |
|         HKEY *out)
 | |
| {
 | |
|     struct reg_hook_key *key;
 | |
|     LSTATUS err;
 | |
|     size_t i;
 | |
| 
 | |
|     *out = NULL;
 | |
| 
 | |
|     for (i = 0 ; i < reg_hook_nkeys ; i++) {
 | |
|         /* Assume reg keys are referenced from a root key and not from some
 | |
|            intermediary key */
 | |
|         key = ®_hook_keys[i];
 | |
| 
 | |
|         if (key->root == parent && wstr_ieq(key->name, name)) {
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /* (Bail out if we didn't find anything; this causes the open/create call
 | |
|        to be passed onward down the hook chain) */
 | |
| 
 | |
|     if (i >= reg_hook_nkeys) {
 | |
|         return ERROR_SUCCESS;
 | |
|     }
 | |
| 
 | |
|     /* Assume only one handle will be open at a time */
 | |
| 
 | |
|     if (key->handle != NULL) {
 | |
|         return ERROR_SHARING_VIOLATION;
 | |
|     }
 | |
| 
 | |
|     /* Open a unique HKEY handle that we can use to identify accesses to
 | |
|        this virtual registry key. We open a read-only handle to an arbitrary
 | |
|        registry key that we can reliably assume exists and isn't one of the
 | |
|        hardcoded root handles. HKLM\SOFTWARE will suffice for this purpose. */
 | |
| 
 | |
|     err = next_RegOpenKeyExW(
 | |
|             HKEY_LOCAL_MACHINE,
 | |
|             L"SOFTWARE",
 | |
|             0,
 | |
|             KEY_READ,
 | |
|             out);
 | |
| 
 | |
|     if (err == ERROR_SUCCESS) {
 | |
|         key->handle = *out;
 | |
|     }
 | |
| 
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegOpenKeyExW(
 | |
|         HKEY parent,
 | |
|         const wchar_t *name,
 | |
|         uint32_t flags,
 | |
|         uint32_t access,
 | |
|         HKEY *out)
 | |
| {
 | |
|     LSTATUS err;
 | |
| 
 | |
|     if (out == NULL) {
 | |
|         return ERROR_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     EnterCriticalSection(®_hook_lock);
 | |
|     err = reg_hook_open_locked(parent, name, out);
 | |
|     LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|     if (err == ERROR_SUCCESS) {
 | |
|         if (*out != NULL) {
 | |
|             //dprintf("Registry: Opened virtual key %S\n", name);
 | |
|         } else {
 | |
|             err = next_RegOpenKeyExW(parent, name, flags, access, out);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegCreateKeyExW(
 | |
|         HKEY parent,
 | |
|         const wchar_t *name,
 | |
|         uint32_t reserved,
 | |
|         const wchar_t *class_,
 | |
|         uint32_t options,
 | |
|         uint32_t access,
 | |
|         const SECURITY_ATTRIBUTES *sa,
 | |
|         HKEY *out,
 | |
|         uint32_t *disposition)
 | |
| {
 | |
|     LSTATUS err;
 | |
| 
 | |
|     if (out == NULL) {
 | |
|         return ERROR_INVALID_PARAMETER;
 | |
|     }
 | |
| 
 | |
|     EnterCriticalSection(®_hook_lock);
 | |
|     err = reg_hook_open_locked(parent, name, out);
 | |
|     LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|     if (err == ERROR_SUCCESS) {
 | |
|         if (*out != NULL) {
 | |
|             //dprintf("Registry: Created virtual key %S\n", name);
 | |
|         } else {
 | |
|             err = next_RegCreateKeyExW(
 | |
|                     parent,
 | |
|                     name,
 | |
|                     reserved,
 | |
|                     class_,
 | |
|                     options,
 | |
|                     access,
 | |
|                     sa,
 | |
|                     out,
 | |
|                     disposition);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegCloseKey(HKEY handle)
 | |
| {
 | |
|     struct reg_hook_key *key;
 | |
|     size_t i;
 | |
| 
 | |
|     EnterCriticalSection(®_hook_lock);
 | |
| 
 | |
|     for (i = 0 ; i < reg_hook_nkeys ; i++) {
 | |
|         key = ®_hook_keys[i];
 | |
| 
 | |
|         if (key->handle == handle) {
 | |
|             //dprintf("Registry: Closed virtual key %S\n", key->name);
 | |
|             key->handle = NULL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|     return next_RegCloseKey(handle);
 | |
| }
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegQueryValueExW(
 | |
|         HKEY handle,
 | |
|         const wchar_t *name,
 | |
|         void *reserved,
 | |
|         uint32_t *type,
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes)
 | |
| {
 | |
|     struct reg_hook_key *key;
 | |
|     LSTATUS err;
 | |
| 
 | |
|     EnterCriticalSection(®_hook_lock);
 | |
| 
 | |
|     key = reg_hook_match_key_locked(handle);
 | |
| 
 | |
|     /* Check if this is a virtualized registry key */
 | |
| 
 | |
|     if (key == NULL) {
 | |
|         LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|         return next_RegQueryValueExW(
 | |
|                 handle,
 | |
|                 name,
 | |
|                 reserved,
 | |
|                 type,
 | |
|                 bytes,
 | |
|                 nbytes);
 | |
|     }
 | |
| 
 | |
|     /* Call the factored out core of this function because RegQueryValueExA
 | |
|        has to be a blight upon my existence */
 | |
| 
 | |
|     err = reg_hook_query_val_locked(key, name, type, bytes, nbytes);
 | |
| 
 | |
|     LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| /* now this right here is a pain in my ass */
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegQueryValueExA(
 | |
|         HKEY handle,
 | |
|         const char *name,
 | |
|         void *reserved,
 | |
|         uint32_t *type,
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes)
 | |
| {
 | |
|     /* _s: sizeof, _c: _countof(), _w: widened */
 | |
| 
 | |
|     struct reg_hook_key *key;
 | |
|     wchar_t *name_w;
 | |
|     size_t name_c;
 | |
|     wchar_t *content;
 | |
|     uint32_t content_s;
 | |
|     size_t content_c;
 | |
|     uint32_t type_site;
 | |
|     LSTATUS err;
 | |
| 
 | |
|     name_w = NULL;
 | |
|     content = NULL;
 | |
| 
 | |
|     /* Normalize inconvenient inputs */
 | |
| 
 | |
|     if (name == NULL) {
 | |
|         name = "";
 | |
|     }
 | |
| 
 | |
|     if (type == NULL) {
 | |
|         type = &type_site;
 | |
|     }
 | |
| 
 | |
|     /* Look up key handle, early exit if no match */
 | |
| 
 | |
|     EnterCriticalSection(®_hook_lock);
 | |
|     key = reg_hook_match_key_locked(handle);
 | |
| 
 | |
|     if (key == NULL) {
 | |
|         LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|         return next_RegQueryValueExA(
 | |
|                 handle,
 | |
|                 name,
 | |
|                 reserved,
 | |
|                 type,
 | |
|                 bytes,
 | |
|                 nbytes);
 | |
|     }
 | |
| 
 | |
|     /* OK, first off we need to widen the name. This requires a temporary
 | |
|        buffer allocation. */
 | |
| 
 | |
|     mbstowcs_s(&name_c, NULL, 0, name, 0);
 | |
|     name_w = malloc(name_c * sizeof(wchar_t));
 | |
| 
 | |
|     if (name_w == NULL) {
 | |
|         err = ERROR_OUTOFMEMORY;
 | |
| 
 | |
|         goto end;
 | |
|     }
 | |
| 
 | |
|     mbstowcs_s(NULL, name_w, name_c, name, name_c - 1);
 | |
| 
 | |
|     /* Next, check to see if the caller even cares about the content. We can
 | |
|        pass through if they don't. */
 | |
| 
 | |
|     if (bytes == NULL && nbytes == NULL) {
 | |
|         err = reg_hook_query_val_locked(key, name_w, type, NULL, NULL);
 | |
| 
 | |
|         goto end;
 | |
|     }
 | |
| 
 | |
|     /* Next, we need to check the key type to see if it's REG_SZ. */
 | |
| 
 | |
|     err = reg_hook_query_val_locked(key, name_w, type, NULL, NULL);
 | |
| 
 | |
|     if (err != ERROR_SUCCESS) {
 | |
|         goto end;
 | |
|     }
 | |
| 
 | |
|     /* If it is not REG_SZ then pass the content directly.
 | |
|        (We ignore the REG_MULTI_SZ case here). */
 | |
| 
 | |
|     assert(*type != REG_MULTI_SZ);
 | |
| 
 | |
|     if (*type != REG_SZ) {
 | |
|         err = reg_hook_query_val_locked(key, name_w, type, bytes, nbytes);
 | |
| 
 | |
|         goto end;
 | |
|     }
 | |
| 
 | |
|     /* Otherwise things get more complicated. First we must measure the wide-
 | |
|        character length of the value (hopefully said value does not change
 | |
|        under our feet, of course). */
 | |
| 
 | |
|     err = reg_hook_query_val_locked(key, name_w, type, NULL, &content_s);
 | |
| 
 | |
|     if (err != ERROR_SUCCESS) {
 | |
|         goto end;
 | |
|     }
 | |
| 
 | |
|     /* Next, allocate a scratch buffer. Even if the caller doesn't supply an
 | |
|        output buffer we need to know the actual content to be able to size the
 | |
|        narrow version. */
 | |
| 
 | |
|     content = malloc(content_s);
 | |
| 
 | |
|     if (content == NULL) {
 | |
|         err = ERROR_OUTOFMEMORY;
 | |
| 
 | |
|         goto end;
 | |
|     }
 | |
| 
 | |
|     /* Get the data... */
 | |
| 
 | |
|     err = reg_hook_query_val_locked(key, name_w, type, content, &content_s);
 | |
| 
 | |
|     if (err != ERROR_SUCCESS) {
 | |
|         goto end;
 | |
|     }
 | |
| 
 | |
|     /* Now size the corresponding narrow form and return it to the caller */
 | |
| 
 | |
|     wcstombs_s(&content_c, NULL, 0, content, 0);
 | |
| 
 | |
|     if (bytes != NULL) {
 | |
|         if (nbytes == NULL) {
 | |
|             err = ERROR_INVALID_PARAMETER;
 | |
| 
 | |
|             goto end;
 | |
|         }
 | |
| 
 | |
|         if (*nbytes < content_c) {
 | |
|             err = ERROR_MORE_DATA;
 | |
| 
 | |
|             goto end;
 | |
|         }
 | |
| 
 | |
|         wcstombs_s(NULL, bytes, *nbytes, content, content_c - 1);
 | |
|     }
 | |
| 
 | |
|     if (nbytes != NULL) { /* It really should be, based on earlier checks ... */
 | |
|         *nbytes = content_c;
 | |
|     }
 | |
| 
 | |
|     err = ERROR_SUCCESS;
 | |
| 
 | |
| end:
 | |
|     LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|     free(content);
 | |
|     free(name_w);
 | |
| 
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| static LSTATUS reg_hook_query_val_locked(
 | |
|         struct reg_hook_key *key,
 | |
|         const wchar_t *name,
 | |
|         uint32_t *type,
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes)
 | |
| {
 | |
|     const struct reg_hook_val *val;
 | |
|     LSTATUS err;
 | |
|     HRESULT hr;
 | |
| 
 | |
|     val = reg_hook_match_val_locked(key, name);
 | |
| 
 | |
|     if (val != NULL) {
 | |
|         if (type != NULL) {
 | |
|             *type = val->type;
 | |
|         }
 | |
| 
 | |
|         if (val->read != NULL) {
 | |
|             hr = val->read(bytes, nbytes);
 | |
|             err = reg_hook_propagate_hr(hr);
 | |
|         } else {
 | |
|             dprintf("Registry: %S: Val %S has no read handler\n",
 | |
|                     key->name,
 | |
|                     name);
 | |
| 
 | |
|             err = ERROR_ACCESS_DENIED;
 | |
|         }
 | |
|     } else {
 | |
|         dprintf("Registry: Key %S: Val %S not found\n", key->name, name);
 | |
|         err = ERROR_FILE_NOT_FOUND;
 | |
|     }
 | |
| 
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| static LSTATUS WINAPI hook_RegSetValueExW(
 | |
|         HKEY handle,
 | |
|         const wchar_t *name,
 | |
|         uint32_t reserved,
 | |
|         uint32_t type,
 | |
|         const void *bytes,
 | |
|         uint32_t nbytes)
 | |
| {
 | |
|     struct reg_hook_key *key;
 | |
|     const struct reg_hook_val *val;
 | |
|     LSTATUS err;
 | |
|     HRESULT hr;
 | |
| 
 | |
|     EnterCriticalSection(®_hook_lock);
 | |
| 
 | |
|     key = reg_hook_match_key_locked(handle);
 | |
| 
 | |
|     if (key == NULL) {
 | |
|         LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|         return next_RegSetValueExW(
 | |
|                 handle,
 | |
|                 name,
 | |
|                 reserved,
 | |
|                 type,
 | |
|                 bytes,
 | |
|                 nbytes);
 | |
|     }
 | |
| 
 | |
|     val = reg_hook_match_val_locked(key, name);
 | |
| 
 | |
|     if (val != NULL) {
 | |
|         if (val->write != NULL) {
 | |
|             if (type != val->type) {
 | |
|                 dprintf(        "Registry: Key %S: Val %S: Type mismatch "
 | |
|                                 "(expected %i got %i)\n",
 | |
|                         key->name,
 | |
|                         name,
 | |
|                         val->type,
 | |
|                         type);
 | |
| 
 | |
|                 err = ERROR_ACCESS_DENIED;
 | |
|             } else {
 | |
|                 dprintf("Registry: Write virtual key %S value %S\n",
 | |
|                         key->name,
 | |
|                         val->name);
 | |
| 
 | |
|                 hr = val->write(bytes, nbytes);
 | |
|                 err = reg_hook_propagate_hr(hr);
 | |
|             }
 | |
|         } else {
 | |
|             /* No write handler (the common case), black-hole whatever gets
 | |
|                written. */
 | |
| 
 | |
|             err = ERROR_SUCCESS;
 | |
|         }
 | |
|     } else {
 | |
|         dprintf("Registry: Key %S: Val %S not found\n", key->name, name);
 | |
|         err = ERROR_FILE_NOT_FOUND;
 | |
|     }
 | |
| 
 | |
|     LeaveCriticalSection(®_hook_lock);
 | |
| 
 | |
|     return err;
 | |
| }
 | |
| 
 | |
| HRESULT reg_hook_read_bin(
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes,
 | |
|         const void *src_bytes,
 | |
|         size_t src_nbytes)
 | |
| {
 | |
|     assert(src_bytes != NULL || src_nbytes == 0);
 | |
| 
 | |
|     if (bytes != NULL) {
 | |
|         if (nbytes == NULL || *nbytes < src_nbytes) {
 | |
|             return HRESULT_FROM_WIN32(ERROR_MORE_DATA);
 | |
|         }
 | |
| 
 | |
|         memcpy(bytes, src_bytes, src_nbytes);
 | |
|     }
 | |
| 
 | |
|     if (nbytes != NULL) {
 | |
|         *nbytes = src_nbytes;
 | |
|     }
 | |
| 
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| HRESULT reg_hook_read_u32(
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes,
 | |
|         uint32_t src)
 | |
| {
 | |
|     if (bytes != NULL) {
 | |
|         if (nbytes == NULL || *nbytes < sizeof(uint32_t)) {
 | |
|             return HRESULT_FROM_WIN32(ERROR_MORE_DATA);
 | |
|         }
 | |
| 
 | |
|         memcpy(bytes, &src, sizeof(uint32_t));
 | |
|     }
 | |
| 
 | |
|     if (nbytes != NULL) {
 | |
|         *nbytes = sizeof(uint32_t);
 | |
|     }
 | |
| 
 | |
|     return S_OK;
 | |
| }
 | |
| 
 | |
| HRESULT reg_hook_read_wstr(
 | |
|         void *bytes,
 | |
|         uint32_t *nbytes,
 | |
|         const wchar_t *src)
 | |
| {
 | |
|     size_t src_nbytes;
 | |
| 
 | |
|     assert(src != NULL);
 | |
| 
 | |
|     src_nbytes = (wcslen(src) + 1) * sizeof(wchar_t);
 | |
| 
 | |
|     if (bytes != NULL) {
 | |
|         if (nbytes == NULL || *nbytes < src_nbytes) {
 | |
|             return HRESULT_FROM_WIN32(ERROR_MORE_DATA);
 | |
|         }
 | |
| 
 | |
|         memcpy(bytes, src, src_nbytes);
 | |
|     }
 | |
| 
 | |
|     if (nbytes != NULL) {
 | |
|         *nbytes = src_nbytes;
 | |
|     }
 | |
| 
 | |
|     return S_OK;
 | |
| }
 |