| Index: chrome_elf/nt_registry/nt_registry.cc
|
| diff --git a/chrome_elf/nt_registry/nt_registry.cc b/chrome_elf/nt_registry/nt_registry.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..373f00945682e20e1c1108a244015bc4ec43ae2e
|
| --- /dev/null
|
| +++ b/chrome_elf/nt_registry/nt_registry.cc
|
| @@ -0,0 +1,685 @@
|
| +// Copyright 2016 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome_elf/nt_registry/nt_registry.h"
|
| +
|
| +namespace {
|
| +
|
| +// Function pointers used for registry access.
|
| +RtlInitUnicodeStringFunction g_rtl_init_unicode_string = nullptr;
|
| +NtCreateKeyFunction g_nt_create_key = nullptr;
|
| +NtDeleteKeyFunction g_nt_delete_key = nullptr;
|
| +NtOpenKeyExFunction g_nt_open_key_ex = nullptr;
|
| +NtCloseFunction g_nt_close = nullptr;
|
| +NtQueryValueKeyFunction g_nt_query_value_key = nullptr;
|
| +NtSetValueKeyFunction g_nt_set_value_key = nullptr;
|
| +
|
| +// Lazy init. No concern about concurrency in chrome_elf.
|
| +bool g_initialized = false;
|
| +bool g_system_install = false;
|
| +bool g_reg_redirection = false;
|
| +const size_t g_kMaxPathLen = 255;
|
| +wchar_t g_kRegPathHKLM[] = L"\\Registry\\Machine\\";
|
| +wchar_t g_kRegPathHKCU[g_kMaxPathLen] = L"";
|
| +wchar_t g_current_user_sid_string[g_kMaxPathLen] = L"";
|
| +wchar_t g_override_path[g_kMaxPathLen] = L"";
|
| +
|
| +// Not using install_util, to prevent circular dependency.
|
| +bool IsThisProcSystem() {
|
| + wchar_t program_dir[MAX_PATH] = {};
|
| + wchar_t* cmd_line = GetCommandLineW();
|
| + // If our command line starts with the "Program Files" or
|
| + // "Program Files (x86)" path, we're system.
|
| + DWORD ret = ::GetEnvironmentVariable(L"PROGRAMFILES", program_dir, MAX_PATH);
|
| + if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
|
| + return true;
|
| +
|
| + ret = ::GetEnvironmentVariable(L"PROGRAMFILES(X86)", program_dir, MAX_PATH);
|
| + if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
|
| + return true;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool InitNativeRegApi() {
|
| + HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
|
| +
|
| + // Setup the global function pointers for registry access.
|
| + g_rtl_init_unicode_string = reinterpret_cast<RtlInitUnicodeStringFunction>(
|
| + ::GetProcAddress(ntdll, "RtlInitUnicodeString"));
|
| +
|
| + g_nt_create_key = reinterpret_cast<NtCreateKeyFunction>(
|
| + ::GetProcAddress(ntdll, "NtCreateKey"));
|
| +
|
| + g_nt_delete_key = reinterpret_cast<NtDeleteKeyFunction>(
|
| + ::GetProcAddress(ntdll, "NtDeleteKey"));
|
| +
|
| + g_nt_open_key_ex = reinterpret_cast<NtOpenKeyExFunction>(
|
| + ::GetProcAddress(ntdll, "NtOpenKeyEx"));
|
| +
|
| + g_nt_close =
|
| + reinterpret_cast<NtCloseFunction>(::GetProcAddress(ntdll, "NtClose"));
|
| +
|
| + g_nt_query_value_key = reinterpret_cast<NtQueryValueKeyFunction>(
|
| + ::GetProcAddress(ntdll, "NtQueryValueKey"));
|
| +
|
| + g_nt_set_value_key = reinterpret_cast<NtSetValueKeyFunction>(
|
| + ::GetProcAddress(ntdll, "NtSetValueKey"));
|
| +
|
| + if (!g_rtl_init_unicode_string || !g_nt_create_key || !g_nt_open_key_ex ||
|
| + !g_nt_delete_key || !g_nt_close || !g_nt_query_value_key ||
|
| + !g_nt_set_value_key)
|
| + return false;
|
| +
|
| + // We need to set HKCU based on the sid of the current user account.
|
| + RtlFormatCurrentUserKeyPathFunction rtl_current_user_string =
|
| + reinterpret_cast<RtlFormatCurrentUserKeyPathFunction>(
|
| + ::GetProcAddress(ntdll, "RtlFormatCurrentUserKeyPath"));
|
| +
|
| + RtlFreeUnicodeStringFunction rtl_free_unicode_str =
|
| + reinterpret_cast<RtlFreeUnicodeStringFunction>(
|
| + ::GetProcAddress(ntdll, "RtlFreeUnicodeString"));
|
| +
|
| + if (!rtl_current_user_string || !rtl_free_unicode_str)
|
| + return false;
|
| +
|
| + UNICODE_STRING current_user_reg_path;
|
| + if (!NT_SUCCESS(rtl_current_user_string(¤t_user_reg_path)))
|
| + return false;
|
| +
|
| + // Finish setting up global HKCU path.
|
| + ::wcsncat(g_kRegPathHKCU, current_user_reg_path.Buffer, (g_kMaxPathLen - 1));
|
| + ::wcsncat(g_kRegPathHKCU, L"\\",
|
| + (g_kMaxPathLen - ::wcslen(g_kRegPathHKCU) - 1));
|
| + // Keep the sid string as well.
|
| + wchar_t* ptr = ::wcsrchr(current_user_reg_path.Buffer, L'\\');
|
| + ptr++;
|
| + ::wcsncpy(g_current_user_sid_string, ptr, (g_kMaxPathLen - 1));
|
| + rtl_free_unicode_str(¤t_user_reg_path);
|
| +
|
| + // Figure out if we're a system or user install.
|
| + g_system_install = IsThisProcSystem();
|
| +
|
| + g_initialized = true;
|
| + return true;
|
| +}
|
| +
|
| +const wchar_t* ConvertRootKey(nt::ROOT_KEY root) {
|
| + nt::ROOT_KEY key = root;
|
| +
|
| + if (!root) {
|
| + // AUTO
|
| + key = g_system_install ? nt::HKLM : nt::HKCU;
|
| + }
|
| +
|
| + if ((key == nt::HKCU) && (::wcslen(nt::HKCU_override) != 0)) {
|
| + std::wstring temp(g_kRegPathHKCU);
|
| + temp.append(nt::HKCU_override);
|
| + temp.append(L"\\");
|
| + ::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1);
|
| + g_reg_redirection = true;
|
| + return g_override_path;
|
| + } else if ((key == nt::HKLM) && (::wcslen(nt::HKLM_override) != 0)) {
|
| + std::wstring temp(g_kRegPathHKCU);
|
| + temp.append(nt::HKLM_override);
|
| + temp.append(L"\\");
|
| + ::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1);
|
| + g_reg_redirection = true;
|
| + return g_override_path;
|
| + }
|
| +
|
| + g_reg_redirection = false;
|
| + if (key == nt::HKCU)
|
| + return g_kRegPathHKCU;
|
| + else
|
| + return g_kRegPathHKLM;
|
| +}
|
| +
|
| +// Turns a root and subkey path into the registry base hive and the rest of the
|
| +// subkey tokens.
|
| +// - |converted_root| should come directly out of ConvertRootKey function.
|
| +// - E.g. base hive: "\Registry\Machine\", "\Registry\User\<SID>\".
|
| +bool ParseFullRegPath(const wchar_t* converted_root,
|
| + const wchar_t* subkey_path,
|
| + std::wstring* out_base,
|
| + std::vector<std::wstring>* subkeys) {
|
| + out_base->clear();
|
| + subkeys->clear();
|
| + std::wstring temp = L"";
|
| +
|
| + if (g_reg_redirection) {
|
| + // Why process |converted_root|? To handle reg redirection used by tests.
|
| + // E.g.:
|
| + // |converted_root| = "\REGISTRY\USER\S-1-5-21-39260824-743453154-142223018-
|
| + // 716772\Software\Chromium\TempTestKeys\13110669370890870$94c6ed9d-bc34-
|
| + // 44f3-a0b3-9eee2d3f2f82\".
|
| + // |subkey_path| = "SOFTWARE\Google\Chrome\BrowserSec".
|
| + temp.append(converted_root);
|
| + }
|
| + if (subkey_path != nullptr)
|
| + temp.append(subkey_path);
|
| +
|
| + // Tokenize the full path.
|
| + size_t find_start = 0;
|
| + size_t delimiter = temp.find_first_of(L'\\');
|
| + while (delimiter != std::wstring::npos) {
|
| + std::wstring token = temp.substr(find_start, delimiter - find_start);
|
| + if (!token.empty())
|
| + subkeys->push_back(token);
|
| + find_start = delimiter + 1;
|
| + delimiter = temp.find_first_of(L'\\', find_start);
|
| + }
|
| + if (!temp.empty() && find_start < temp.length())
|
| + // Get the last token.
|
| + subkeys->push_back(temp.substr(find_start));
|
| +
|
| + if (g_reg_redirection) {
|
| + // The base hive for HKCU needs to include the user SID.
|
| + uint32_t num_base_tokens = 2;
|
| + const wchar_t* hkcu = L"\\REGISTRY\\USER\\";
|
| + if (0 == ::wcsnicmp(converted_root, hkcu, ::wcslen(hkcu)))
|
| + num_base_tokens = 3;
|
| +
|
| + if (subkeys->size() < num_base_tokens)
|
| + return false;
|
| +
|
| + // Pull out the base hive tokens.
|
| + out_base->push_back(L'\\');
|
| + for (size_t i = 0; i < num_base_tokens; i++) {
|
| + out_base->append((*subkeys)[i].c_str());
|
| + out_base->push_back(L'\\');
|
| + }
|
| + subkeys->erase(subkeys->begin(), subkeys->begin() + num_base_tokens);
|
| + } else {
|
| + out_base->assign(converted_root);
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +NTSTATUS CreateKeyWrapper(const std::wstring& key_path,
|
| + ACCESS_MASK access,
|
| + HANDLE* out_handle,
|
| + ULONG* create_or_open OPTIONAL) {
|
| + UNICODE_STRING key_path_uni = {};
|
| + g_rtl_init_unicode_string(&key_path_uni, key_path.c_str());
|
| +
|
| + OBJECT_ATTRIBUTES obj = {};
|
| + InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
|
| + nullptr);
|
| +
|
| + return g_nt_create_key(out_handle, access, &obj, 0, nullptr,
|
| + REG_OPTION_NON_VOLATILE, create_or_open);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +namespace nt {
|
| +
|
| +const size_t g_kRegMaxPathLen = 255;
|
| +wchar_t HKLM_override[g_kRegMaxPathLen] = L"";
|
| +wchar_t HKCU_override[g_kRegMaxPathLen] = L"";
|
| +
|
| +//------------------------------------------------------------------------------
|
| +// Create, open, delete, close functions
|
| +//------------------------------------------------------------------------------
|
| +
|
| +bool CreateRegKey(ROOT_KEY root,
|
| + const wchar_t* key_path,
|
| + ACCESS_MASK access,
|
| + HANDLE* out_handle OPTIONAL) {
|
| + if (!g_initialized)
|
| + InitNativeRegApi();
|
| +
|
| + std::wstring current_path;
|
| + std::vector<std::wstring> subkeys;
|
| + if (!ParseFullRegPath(ConvertRootKey(root), key_path, ¤t_path,
|
| + &subkeys))
|
| + return false;
|
| +
|
| + // Open the base hive first. It should always exist already.
|
| + HANDLE last_handle = INVALID_HANDLE_VALUE;
|
| + NTSTATUS status =
|
| + CreateKeyWrapper(current_path, access, &last_handle, nullptr);
|
| + if (!NT_SUCCESS(status))
|
| + return false;
|
| +
|
| + size_t subkeys_size = subkeys.size();
|
| + if (subkeys_size != 0)
|
| + g_nt_close(last_handle);
|
| +
|
| + // Recursively open/create each subkey.
|
| + std::vector<HANDLE> rollback;
|
| + bool success = true;
|
| + for (size_t i = 0; i < subkeys_size; i++) {
|
| + current_path.append(subkeys[i]);
|
| + current_path.push_back(L'\\');
|
| +
|
| + // Process the latest subkey.
|
| + ULONG created = 0;
|
| + HANDLE key_handle = INVALID_HANDLE_VALUE;
|
| + status =
|
| + CreateKeyWrapper(current_path.c_str(), access, &key_handle, &created);
|
| + if (!NT_SUCCESS(status)) {
|
| + success = false;
|
| + break;
|
| + }
|
| +
|
| + if (i == subkeys_size - 1) {
|
| + last_handle = key_handle;
|
| + } else {
|
| + // Save any subkey handle created, in case of rollback.
|
| + if (created == REG_CREATED_NEW_KEY)
|
| + rollback.push_back(key_handle);
|
| + else
|
| + g_nt_close(key_handle);
|
| + }
|
| + }
|
| +
|
| + if (!success) {
|
| + // Delete any subkeys created.
|
| + for (HANDLE handle : rollback) {
|
| + g_nt_delete_key(handle);
|
| + }
|
| + }
|
| + for (HANDLE handle : rollback) {
|
| + // Close the rollback handles, on success or failure.
|
| + g_nt_close(handle);
|
| + }
|
| + if (!success)
|
| + return false;
|
| +
|
| + // See if caller wants the handle left open.
|
| + if (out_handle)
|
| + *out_handle = last_handle;
|
| + else
|
| + g_nt_close(last_handle);
|
| +
|
| + return true;
|
| +}
|
| +
|
| +bool OpenRegKey(ROOT_KEY root,
|
| + const wchar_t* key_path,
|
| + ACCESS_MASK access,
|
| + HANDLE* out_handle,
|
| + NTSTATUS* error_code OPTIONAL) {
|
| + if (!g_initialized)
|
| + InitNativeRegApi();
|
| +
|
| + NTSTATUS status = STATUS_UNSUCCESSFUL;
|
| + UNICODE_STRING key_path_uni = {};
|
| + OBJECT_ATTRIBUTES obj = {};
|
| + *out_handle = INVALID_HANDLE_VALUE;
|
| +
|
| + std::wstring full_path(ConvertRootKey(root));
|
| + full_path.append(key_path);
|
| +
|
| + g_rtl_init_unicode_string(&key_path_uni, full_path.c_str());
|
| + InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
|
| + NULL);
|
| +
|
| + status = g_nt_open_key_ex(out_handle, access, &obj, 0);
|
| + // See if caller wants the NTSTATUS.
|
| + if (error_code)
|
| + *error_code = status;
|
| +
|
| + if (NT_SUCCESS(status))
|
| + return true;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +bool DeleteRegKey(HANDLE key) {
|
| + if (!g_initialized)
|
| + InitNativeRegApi();
|
| +
|
| + NTSTATUS status = STATUS_UNSUCCESSFUL;
|
| +
|
| + status = g_nt_delete_key(key);
|
| +
|
| + if (NT_SUCCESS(status))
|
| + return true;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool DeleteRegKey(ROOT_KEY root, const wchar_t* key_path) {
|
| + HANDLE key = INVALID_HANDLE_VALUE;
|
| +
|
| + if (!OpenRegKey(root, key_path, DELETE, &key, nullptr))
|
| + return false;
|
| +
|
| + if (!DeleteRegKey(key)) {
|
| + CloseRegKey(key);
|
| + return false;
|
| + }
|
| +
|
| + CloseRegKey(key);
|
| + return true;
|
| +}
|
| +
|
| +void CloseRegKey(HANDLE key) {
|
| + if (!g_initialized)
|
| + InitNativeRegApi();
|
| + g_nt_close(key);
|
| +}
|
| +
|
| +//------------------------------------------------------------------------------
|
| +// Getter functions
|
| +//------------------------------------------------------------------------------
|
| +
|
| +bool QueryRegKeyValue(HANDLE key,
|
| + const wchar_t* value_name,
|
| + ULONG* out_type,
|
| + BYTE** out_buffer,
|
| + DWORD* out_size) {
|
| + if (!g_initialized)
|
| + InitNativeRegApi();
|
| +
|
| + NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
|
| + UNICODE_STRING value_uni = {};
|
| + g_rtl_init_unicode_string(&value_uni, value_name);
|
| + DWORD size_needed = 0;
|
| + bool success = false;
|
| +
|
| + // First call to find out how much room we need for the value!
|
| + ntstatus = g_nt_query_value_key(key, &value_uni, KeyValueFullInformation,
|
| + nullptr, 0, &size_needed);
|
| + if (ntstatus != STATUS_BUFFER_TOO_SMALL)
|
| + return false;
|
| +
|
| + KEY_VALUE_FULL_INFORMATION* value_info =
|
| + reinterpret_cast<KEY_VALUE_FULL_INFORMATION*>(new BYTE[size_needed]);
|
| +
|
| + // Second call to get the value.
|
| + ntstatus = g_nt_query_value_key(key, &value_uni, KeyValueFullInformation,
|
| + value_info, size_needed, &size_needed);
|
| + if (NT_SUCCESS(ntstatus)) {
|
| + *out_type = value_info->Type;
|
| + *out_size = value_info->DataLength;
|
| + *out_buffer = new BYTE[*out_size];
|
| + ::memcpy(*out_buffer,
|
| + (reinterpret_cast<BYTE*>(value_info) + value_info->DataOffset),
|
| + *out_size);
|
| + success = true;
|
| + }
|
| +
|
| + delete[] value_info;
|
| + return success;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool QueryRegValueDWORD(HANDLE key,
|
| + const wchar_t* value_name,
|
| + DWORD* out_dword) {
|
| + ULONG type = REG_NONE;
|
| + BYTE* value_bytes = nullptr;
|
| + DWORD ret_size = 0;
|
| +
|
| + if (!QueryRegKeyValue(key, value_name, &type, &value_bytes, &ret_size) ||
|
| + type != REG_DWORD)
|
| + return false;
|
| +
|
| + *out_dword = *(reinterpret_cast<DWORD*>(value_bytes));
|
| +
|
| + delete[] value_bytes;
|
| + return true;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool QueryRegValueDWORD(ROOT_KEY root,
|
| + const wchar_t* key_path,
|
| + const wchar_t* value_name,
|
| + DWORD* out_dword) {
|
| + HANDLE key = INVALID_HANDLE_VALUE;
|
| +
|
| + if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key,
|
| + NULL))
|
| + return false;
|
| +
|
| + if (!QueryRegValueDWORD(key, value_name, out_dword)) {
|
| + CloseRegKey(key);
|
| + return false;
|
| + }
|
| +
|
| + CloseRegKey(key);
|
| + return true;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool QueryRegValueSZ(HANDLE key,
|
| + const wchar_t* value_name,
|
| + std::wstring* out_sz) {
|
| + BYTE* value_bytes = nullptr;
|
| + DWORD ret_size = 0;
|
| + ULONG type = REG_NONE;
|
| +
|
| + if (!QueryRegKeyValue(key, value_name, &type, &value_bytes, &ret_size) ||
|
| + type != REG_SZ)
|
| + return false;
|
| +
|
| + *out_sz = reinterpret_cast<wchar_t*>(value_bytes);
|
| +
|
| + delete[] value_bytes;
|
| + return true;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool QueryRegValueSZ(ROOT_KEY root,
|
| + const wchar_t* key_path,
|
| + const wchar_t* value_name,
|
| + std::wstring* out_sz) {
|
| + HANDLE key = INVALID_HANDLE_VALUE;
|
| +
|
| + if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key,
|
| + NULL))
|
| + return false;
|
| +
|
| + if (!QueryRegValueSZ(key, value_name, out_sz)) {
|
| + CloseRegKey(key);
|
| + return false;
|
| + }
|
| +
|
| + CloseRegKey(key);
|
| + return true;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool QueryRegValueMULTISZ(HANDLE key,
|
| + const wchar_t* value_name,
|
| + std::vector<std::wstring>* out_multi_sz) {
|
| + BYTE* value_bytes = nullptr;
|
| + DWORD ret_size = 0;
|
| + ULONG type = REG_NONE;
|
| +
|
| + if (!QueryRegKeyValue(key, value_name, &type, &value_bytes, &ret_size) ||
|
| + type != REG_MULTI_SZ)
|
| + return false;
|
| +
|
| + // Make sure the vector is empty to start.
|
| + (*out_multi_sz).resize(0);
|
| +
|
| + wchar_t* pointer = reinterpret_cast<wchar_t*>(value_bytes);
|
| + std::wstring temp = pointer;
|
| + // Loop. Each string is separated by '\0'. Another '\0' at very end (so 2 in
|
| + // a row).
|
| + while (temp.length() != 0) {
|
| + (*out_multi_sz).push_back(temp);
|
| +
|
| + pointer += temp.length() + 1;
|
| + temp = pointer;
|
| + }
|
| +
|
| + // Handle the case of "empty multi_sz".
|
| + if (out_multi_sz->size() == 0)
|
| + out_multi_sz->push_back(L"");
|
| +
|
| + delete[] value_bytes;
|
| + return true;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool QueryRegValueMULTISZ(ROOT_KEY root,
|
| + const wchar_t* key_path,
|
| + const wchar_t* value_name,
|
| + std::vector<std::wstring>* out_multi_sz) {
|
| + HANDLE key = INVALID_HANDLE_VALUE;
|
| +
|
| + if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key,
|
| + NULL))
|
| + return false;
|
| +
|
| + if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) {
|
| + CloseRegKey(key);
|
| + return false;
|
| + }
|
| +
|
| + CloseRegKey(key);
|
| + return true;
|
| +}
|
| +
|
| +//------------------------------------------------------------------------------
|
| +// Setter functions
|
| +//------------------------------------------------------------------------------
|
| +
|
| +bool SetRegKeyValue(HANDLE key,
|
| + const wchar_t* value_name,
|
| + ULONG type,
|
| + const BYTE* data,
|
| + DWORD data_size) {
|
| + if (!g_initialized)
|
| + InitNativeRegApi();
|
| +
|
| + NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
|
| + UNICODE_STRING value_uni = {};
|
| + g_rtl_init_unicode_string(&value_uni, value_name);
|
| +
|
| + BYTE* non_const_data = const_cast<BYTE*>(data);
|
| + ntstatus =
|
| + g_nt_set_value_key(key, &value_uni, 0, type, non_const_data, data_size);
|
| +
|
| + if (NT_SUCCESS(ntstatus))
|
| + return true;
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) {
|
| + return SetRegKeyValue(key, value_name, REG_DWORD,
|
| + reinterpret_cast<BYTE*>(&value), sizeof(value));
|
| +}
|
| +
|
| +// wrapper function
|
| +bool SetRegValueDWORD(ROOT_KEY root,
|
| + const wchar_t* key_path,
|
| + const wchar_t* value_name,
|
| + DWORD value) {
|
| + HANDLE key = INVALID_HANDLE_VALUE;
|
| +
|
| + if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL))
|
| + return false;
|
| +
|
| + if (!SetRegValueDWORD(key, value_name, value)) {
|
| + CloseRegKey(key);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool SetRegValueSZ(HANDLE key,
|
| + const wchar_t* value_name,
|
| + const std::wstring& value) {
|
| + // Make sure the number of bytes in |value|, including EoS, fits in a DWORD.
|
| + if (std::numeric_limits<DWORD>::max() <
|
| + ((value.length() + 1) * sizeof(wchar_t)))
|
| + return false;
|
| +
|
| + DWORD size = (static_cast<DWORD>((value.length() + 1) * sizeof(wchar_t)));
|
| + return SetRegKeyValue(key, value_name, REG_SZ,
|
| + reinterpret_cast<const BYTE*>(value.c_str()), size);
|
| +}
|
| +
|
| +// wrapper function
|
| +bool SetRegValueSZ(ROOT_KEY root,
|
| + const wchar_t* key_path,
|
| + const wchar_t* value_name,
|
| + const std::wstring& value) {
|
| + HANDLE key = INVALID_HANDLE_VALUE;
|
| +
|
| + if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL))
|
| + return false;
|
| +
|
| + if (!SetRegValueSZ(key, value_name, value)) {
|
| + CloseRegKey(key);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +// wrapper function
|
| +bool SetRegValueMULTISZ(HANDLE key,
|
| + const wchar_t* value_name,
|
| + const std::vector<std::wstring>& values) {
|
| + std::vector<wchar_t> builder;
|
| +
|
| + for (auto& string : values) {
|
| + // Just in case someone is passing in an illegal empty string
|
| + // (not allowed in REG_MULTI_SZ), ignore it.
|
| + if (!string.empty()) {
|
| + for (const wchar_t& w : string) {
|
| + builder.push_back(w);
|
| + }
|
| + builder.push_back(L'\0');
|
| + }
|
| + }
|
| + // Add second null terminator to end REG_MULTI_SZ.
|
| + builder.push_back(L'\0');
|
| + // Handle rare case where the vector passed in was empty,
|
| + // or only had an empty string.
|
| + if (builder.size() == 1)
|
| + builder.push_back(L'\0');
|
| +
|
| + if (std::numeric_limits<DWORD>::max() < builder.size())
|
| + return false;
|
| +
|
| + return SetRegKeyValue(
|
| + key, value_name, REG_MULTI_SZ, reinterpret_cast<BYTE*>(builder.data()),
|
| + (static_cast<DWORD>(builder.size()) + 1) * sizeof(wchar_t));
|
| +}
|
| +
|
| +// wrapper function
|
| +bool SetRegValueMULTISZ(ROOT_KEY root,
|
| + const wchar_t* key_path,
|
| + const wchar_t* value_name,
|
| + const std::vector<std::wstring>& values) {
|
| + HANDLE key = INVALID_HANDLE_VALUE;
|
| +
|
| + if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL))
|
| + return false;
|
| +
|
| + if (!SetRegValueMULTISZ(key, value_name, values)) {
|
| + CloseRegKey(key);
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +//------------------------------------------------------------------------------
|
| +// Utils
|
| +//------------------------------------------------------------------------------
|
| +
|
| +const wchar_t* GetCurrentUserSidString() {
|
| + if (!g_initialized)
|
| + InitNativeRegApi();
|
| +
|
| + return g_current_user_sid_string;
|
| +}
|
| +
|
| +}; // namespace nt
|
|
|