Chromium Code Reviews| 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 |
| index 373f00945682e20e1c1108a244015bc4ec43ae2e..09af8b9894483791050199a67e046111a6cd96a7 100644 |
| --- a/chrome_elf/nt_registry/nt_registry.cc |
| +++ b/chrome_elf/nt_registry/nt_registry.cc |
| @@ -4,6 +4,9 @@ |
| #include "chrome_elf/nt_registry/nt_registry.h" |
| +#include <assert.h> |
| +#include <stdlib.h> |
| + |
| namespace { |
| // Function pointers used for registry access. |
| @@ -18,14 +21,22 @@ 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_wow64_proc = 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""; |
| +wchar_t g_kRegPathHKCU[nt::g_kRegMaxPathLen + 1] = L""; |
| +wchar_t g_current_user_sid_string[nt::g_kRegMaxPathLen + 1] = L""; |
| +wchar_t g_override_path[nt::g_kRegMaxPathLen + 1] = L""; |
| + |
| +// For testing only. |
| +wchar_t g_HKLM_override[nt::g_kRegMaxPathLen + 1] = L""; |
| +wchar_t g_HKCU_override[nt::g_kRegMaxPathLen + 1] = L""; |
| + |
| +//------------------------------------------------------------------------------ |
| +// Initialization - LOCAL |
| +//------------------------------------------------------------------------------ |
| -// Not using install_util, to prevent circular dependency. |
| +// Not using install_static, to prevent circular dependency. |
| bool IsThisProcSystem() { |
| wchar_t program_dir[MAX_PATH] = {}; |
| wchar_t* cmd_line = GetCommandLineW(); |
| @@ -42,6 +53,22 @@ bool IsThisProcSystem() { |
| return false; |
| } |
| +bool IsThisProcWow64() { |
| + // Using BOOL type for compat with IsWow64Process() system API. |
| + BOOL is_wow64 = FALSE; |
| + |
| + // API might not exist, so dynamic lookup. |
| + using IsWow64ProcessFunction = decltype(&IsWow64Process); |
| + IsWow64ProcessFunction is_wow64_process = |
| + reinterpret_cast<IsWow64ProcessFunction>(::GetProcAddress( |
| + ::GetModuleHandle(L"kernel32.dll"), "IsWow64Process")); |
| + if (!is_wow64_process) |
| + return false; |
| + if (!is_wow64_process(::GetCurrentProcess(), &is_wow64)) |
| + return false; |
| + return is_wow64 ? true : false; |
| +} |
| + |
| bool InitNativeRegApi() { |
| HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll"); |
| @@ -89,42 +116,344 @@ bool InitNativeRegApi() { |
| return false; |
| // Finish setting up global HKCU path. |
| - ::wcsncat(g_kRegPathHKCU, current_user_reg_path.Buffer, (g_kMaxPathLen - 1)); |
| + ::wcsncat(g_kRegPathHKCU, current_user_reg_path.Buffer, nt::g_kRegMaxPathLen); |
| ::wcsncat(g_kRegPathHKCU, L"\\", |
| - (g_kMaxPathLen - ::wcslen(g_kRegPathHKCU) - 1)); |
| + (nt::g_kRegMaxPathLen - ::wcslen(g_kRegPathHKCU))); |
| // 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)); |
| + ::wcsncpy(g_current_user_sid_string, ptr, nt::g_kRegMaxPathLen); |
| rtl_free_unicode_str(¤t_user_reg_path); |
| - // Figure out if we're a system or user install. |
| + // Figure out if this is a system or user install. |
| g_system_install = IsThisProcSystem(); |
| + // Figure out if this is a WOW64 process. |
| + g_wow64_proc = IsThisProcWow64(); |
| + |
| g_initialized = true; |
| return true; |
| } |
| +//------------------------------------------------------------------------------ |
| +// Reg WOW64 Redirection - LOCAL |
| +// |
| +// How registry redirection works directly calling NTDLL APIs: |
| +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| +// - NOTE: On >= Win7, reflection support was removed. |
| +// - |
| +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa384253(v=vs.85).aspx |
| +// |
| +// - 1) 32-bit / WOW64 process: |
| +// a) Default access WILL be redirected to WOW64. |
| +// b) KEY_WOW64_32KEY access WILL be redirected to WOW64. |
| +// c) KEY_WOW64_64KEY access will NOT be redirected to WOW64. |
| +// |
| +// - 2) 64-bit process: |
| +// a) Default access will NOT be redirected to WOW64. |
| +// b) KEY_WOW64_32KEY access will NOT be redirected to WOW64. |
| +// c) KEY_WOW64_64KEY access will NOT be redirected to WOW64. |
| +// |
| +// - Key point from above is that NTDLL redirects and respects access |
| +// overrides for WOW64 calling processes. But does NOT do any of that if the |
| +// calling process is 64-bit. 2b is surprising and troublesome. |
| +// |
| +// How registry redirection works using these nt_registry APIs: |
| +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| +// - These APIs will behave the same as NTDLL above, EXCEPT for 2b. |
| +// nt_registry APIs will respect the override access flags for all processes. |
| +// |
| +// - How the WOW64 redirection decision trees / Nodes work below: |
| +// |
| +// The HKLM and HKCU decision trees represent the information at the MSDN |
| +// link above... but in a way that generates a decision about whether a |
| +// registry path should be subject to WOW64 redirection. The tree is |
| +// traversed as you scan along the registry path in question. |
| +// |
| +// - Each Node contains a chunk of registry subkey(s) to match. |
| +// - If it is NOT matched, traversal is done. |
| +// - If it is matched: |
| +// - Current state of |redirection_type| for the whole registry path is |
| +// updated. |
| +// - If |next| is empty, traversal is done. |
| +// - Otherwise, |next| is an array of child Nodes to try to match against. |
| +// Loop. |
| +//------------------------------------------------------------------------------ |
| + |
| +// This enum defines states for how to handle redirection. |
| +// NOTE: When WOW64 redirection should happen, the redirect subkey can be either |
| +// before or after the latest Node match. Unfortunately not consistent. |
| +enum RedirectionType { SHARED = 0, REDIRECTED_BEFORE, REDIRECTED_AFTER }; |
| + |
| +struct Node { |
| + template <size_t len, size_t n_len> |
| + constexpr Node(const wchar_t (&wcs)[len], |
| + RedirectionType rt, |
| + const Node (&n)[n_len]) |
| + : to_match(wcs), |
| + to_match_len(len - 1), |
| + redirection_type(rt), |
| + next(n), |
| + next_len(n_len) {} |
| + |
| + template <size_t len> |
| + constexpr Node(const wchar_t (&wcs)[len], RedirectionType rt) |
| + : to_match(wcs), |
| + to_match_len(len - 1), |
| + redirection_type(rt), |
| + next(nullptr), |
| + next_len(0) {} |
| + |
| + const wchar_t* to_match; |
| + size_t to_match_len; |
| + // If a match, this is the new state of how to redirect. |
| + RedirectionType redirection_type; |
| + // |next| is nullptr or an array of Nodes of length |array_len|. |
| + const Node* next; |
| + size_t next_len; |
| +}; |
| + |
| +// HKLM or HKCU SOFTWARE\Classes is shared by default. Specific subkeys under |
| +// Classes are redirected to SOFTWARE\WOW6432Node\Classes\<subkey> though. |
| +constexpr Node kClassesSubtree[] = {{L"CLSID", REDIRECTED_BEFORE}, |
| + {L"DirectShow", REDIRECTED_BEFORE}, |
| + {L"Interface", REDIRECTED_BEFORE}, |
| + {L"Media Type", REDIRECTED_BEFORE}, |
| + {L"MediaFoundation", REDIRECTED_BEFORE}}; |
| + |
| +// These specific HKLM\SOFTWARE subkeys are shared. Specific |
| +// subkeys under Classes are redirected though... see classes_subtree. |
| +constexpr Node kHklmSoftwareSubtree[] = { |
| + // TODO(pennymac): when MS fixes compiler bug, or bots are all using clang, |
| + // remove the "Classes" subkeys below and replace with: |
| + // {L"Classes", SHARED, kClassesSubtree}, |
| + // https://connect.microsoft.com/VisualStudio/feedback/details/3104499 |
| + {L"Classes\\CLSID", REDIRECTED_BEFORE}, |
| + {L"Classes\\DirectShow", REDIRECTED_BEFORE}, |
| + {L"Classes\\Interface", REDIRECTED_BEFORE}, |
| + {L"Classes\\Media Type", REDIRECTED_BEFORE}, |
| + {L"Classes\\MediaFoundation", REDIRECTED_BEFORE}, |
| + {L"Classes", SHARED}, |
| + |
| + {L"Clients", SHARED}, |
| + {L"Microsoft\\COM3", SHARED}, |
| + {L"Microsoft\\Cryptography\\Calais\\Current", SHARED}, |
| + {L"Microsoft\\Cryptography\\Calais\\Readers", SHARED}, |
| + {L"Microsoft\\Cryptography\\Services", SHARED}, |
| + |
| + {L"Microsoft\\CTF\\SystemShared", SHARED}, |
| + {L"Microsoft\\CTF\\TIP", SHARED}, |
| + {L"Microsoft\\DFS", SHARED}, |
| + {L"Microsoft\\Driver Signing", SHARED}, |
| + {L"Microsoft\\EnterpriseCertificates", SHARED}, |
| + |
| + {L"Microsoft\\EventSystem", SHARED}, |
| + {L"Microsoft\\MSMQ", SHARED}, |
| + {L"Microsoft\\Non-Driver Signing", SHARED}, |
| + {L"Microsoft\\Notepad\\DefaultFonts", SHARED}, |
| + {L"Microsoft\\OLE", SHARED}, |
| + |
| + {L"Microsoft\\RAS", SHARED}, |
| + {L"Microsoft\\RPC", SHARED}, |
| + {L"Microsoft\\SOFTWARE\\Microsoft\\Shared Tools\\MSInfo", SHARED}, |
| + {L"Microsoft\\SystemCertificates", SHARED}, |
| + {L"Microsoft\\TermServLicensing", SHARED}, |
| + |
| + {L"Microsoft\\Transaction Server", SHARED}, |
| + {L"Microsoft\\Windows\\CurrentVersion\\App Paths", SHARED}, |
| + {L"Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cursors\\Schemes", |
| + SHARED}, |
| + {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers", SHARED}, |
| + {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons", SHARED}, |
| + |
| + {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\KindMap", SHARED}, |
| + {L"Microsoft\\Windows\\CurrentVersion\\Group Policy", SHARED}, |
| + {L"Microsoft\\Windows\\CurrentVersion\\Policies", SHARED}, |
| + {L"Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", SHARED}, |
| + {L"Microsoft\\Windows\\CurrentVersion\\Setup", SHARED}, |
| + |
| + {L"Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\Console", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\FontDpi", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\FontLink", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\FontMapper", SHARED}, |
| + |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\Fonts", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\Gre_Initialize", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options", |
| + SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\LanguagePack", SHARED}, |
| + |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\NetworkCards", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\Perflib", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\Ports", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\Print", SHARED}, |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\ProfileList", SHARED}, |
| + |
| + {L"Microsoft\\Windows NT\\CurrentVersion\\Time Zones", SHARED}, |
| + {L"Policies", SHARED}, |
| + {L"RegisteredApplications", SHARED}}; |
| + |
| +// HKCU is entirely shared, except for a few specific Classes subkeys which |
| +// are redirected. See |classes_subtree|. |
| +constexpr Node kRedirectionDecisionTreeHkcu = {L"SOFTWARE\\Classes", SHARED, |
| + kClassesSubtree}; |
| + |
| +// HKLM\SOFTWARE is redirected by default to SOFTWARE\WOW6432Node. Specific |
| +// subkeys under SOFTWARE are shared though... see |hklm_software_subtree|. |
| +constexpr Node kRedirectionDecisionTreeHklm = {L"SOFTWARE", REDIRECTED_AFTER, |
| + kHklmSoftwareSubtree}; |
| + |
| +// Main redirection handler function. |
| +// If redirection is required, change is made to |subkey_path| in place. |
| +// |
| +// - This function should be called BEFORE concatenating |subkey_path| with the |
| +// root hive or calling ParseFullRegPath(). |
| +// - Also, |subkey_path| should be passed to SanitizeSubkeyPath() before calling |
| +// this function. |
| +void ProcessRedirection(nt::ROOT_KEY root, |
| + ACCESS_MASK access, |
| + std::wstring* subkey_path) { |
| + static constexpr wchar_t kRedirectBefore[] = L"WOW6432Node\\"; |
| + static constexpr wchar_t kRedirectAfter[] = L"\\WOW6432Node"; |
| + |
| + assert(subkey_path != nullptr); |
| + assert(subkey_path->empty() || subkey_path->front() != L'\\'); |
| + assert(subkey_path->empty() || subkey_path->back() != L'\\'); |
| + |
| + // |subkey_path| could legitimately be empty. |
| + if (subkey_path->empty() || |
| + (access & KEY_WOW64_32KEY && access & KEY_WOW64_64KEY)) |
| + return; |
| + |
| + // Convert nt::AUTO to the appropriate root key. |
| + nt::ROOT_KEY temp_root = root; |
| + if (root == nt::AUTO) |
| + temp_root = g_system_install ? nt::HKLM : nt::HKCU; |
| + |
| + // No redirection during testing when there's already an override. |
| + // Otherwise, the testing redirect directory Software\Chromium\TempTestKeys |
| + // would get WOW64 redirected if root_key == HKLM in this function. |
| + if ((temp_root == nt::HKCU && *g_HKCU_override) || |
| + (temp_root == nt::HKLM && *g_HKLM_override)) |
| + return; |
| + |
| + // WOW64 redirection only supported on x64 architecture. Return if x86. |
| + SYSTEM_INFO system_info = {}; |
| + ::GetNativeSystemInfo(&system_info); |
| + if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) |
| + return; |
| + |
| + bool use_wow64 = g_wow64_proc; |
| + // Consider KEY_WOW64_32KEY and KEY_WOW64_64KEY override access flags. |
| + if (access & KEY_WOW64_32KEY) |
| + use_wow64 = true; |
| + if (access & KEY_WOW64_64KEY) |
| + use_wow64 = false; |
| + |
| + // If !use_wow64, there's nothing more to do. |
| + if (!use_wow64) |
| + return; |
| + |
| + // The root of the decision trees are an array of 1. |
| + size_t node_array_len = 1; |
| + // Pick which decision tree to use. |
| + const Node* current_node = (temp_root == nt::HKCU) |
| + ? &kRedirectionDecisionTreeHkcu |
| + : &kRedirectionDecisionTreeHklm; |
| + |
| + // The following loop works on the |subkey_path| from left to right. |
| + // |position| tracks progress along |subkey_path|. |
| + const wchar_t* position = subkey_path->c_str(); |
| + // Hold a count of chars left after position, for efficient calculations. |
| + size_t chars_left = subkey_path->length(); |
| + // |redirect_state| holds the latest state of redirection requirement. |
| + RedirectionType redirect_state = SHARED; |
| + // |insertion_point| tracks latest spot for redirection subkey to be inserted. |
| + const wchar_t* insertion_point = nullptr; |
| + // |insert_string| tracks which redirection string would be inserted. |
| + const wchar_t* insert_string = nullptr; |
| + |
| + size_t node_index = 0; |
| + while (node_index < node_array_len) { |
| + size_t current_to_match_len = current_node->to_match_len; |
| + // Make sure the remainder of the path is at least as long as the current |
| + // subkey to match. |
| + if (chars_left >= current_to_match_len) { |
| + // Do case insensitive comparisons. |
| + if (::wcsnicmp(position, current_node->to_match, current_to_match_len) == |
|
grt (UTC plus 2)
2016/09/30 09:32:16
nit: !::wcsnicmp to make this fit on one line
penny
2016/10/01 01:44:25
Done.
|
| + 0) { |
| + // Make sure not to match on a substring. |
| + if (*(position + current_to_match_len) == L'\\' || |
| + *(position + current_to_match_len) == L'\0') { |
| + // MATCH! |
| + // ------------------------------------------------------------------- |
| + // 1) Update state of redirection. |
| + redirect_state = current_node->redirection_type; |
| + // 1.5) If new state is to redirect, the new insertion point will be |
| + // either right before or right after this match. |
| + if (redirect_state == REDIRECTED_BEFORE) { |
| + insertion_point = position; |
| + insert_string = kRedirectBefore; |
| + } else if (redirect_state == REDIRECTED_AFTER) { |
| + insertion_point = position + current_to_match_len; |
| + insert_string = kRedirectAfter; |
| + } |
| + // 2) Adjust |position| along the subkey path. |
| + position += current_to_match_len; |
| + chars_left -= current_to_match_len; |
| + // 2.5) Increment the position, to move past path seperator(s). |
| + while (*position == L'\\') { |
| + ++position; |
| + --chars_left; |
| + } |
| + // 3) Move our loop parameters to the |next| array of Nodes. |
| + node_array_len = current_node->next_len; |
| + current_node = current_node->next; |
| + node_index = 0; |
| + // 4) Finish this loop and start on new array. |
| + continue; |
| + } |
| + } |
| + } |
| + |
| + // Move to the next node in the array if we didn't match this loop. |
| + ++current_node; |
| + ++node_index; |
| + } |
| + |
| + if (redirect_state == SHARED) |
| + return; |
| + |
| + // Insert the redirection into |subkey_path|, at |insertion_point|. |
| + subkey_path->insert((insertion_point - subkey_path->c_str()), insert_string); |
| +} |
| + |
| +//------------------------------------------------------------------------------ |
| +// Reg Path Utilities - LOCAL |
| +//------------------------------------------------------------------------------ |
| + |
| const wchar_t* ConvertRootKey(nt::ROOT_KEY root) { |
| nt::ROOT_KEY key = root; |
| - if (!root) { |
| - // AUTO |
| + if (root == nt::AUTO) { |
|
grt (UTC plus 2)
2016/09/30 09:32:16
nit: omit braces now that the comment is out of th
penny
2016/10/01 01:44:25
Done.
|
| key = g_system_install ? nt::HKLM : nt::HKCU; |
| } |
| - if ((key == nt::HKCU) && (::wcslen(nt::HKCU_override) != 0)) { |
| + if (key == nt::HKCU && *g_HKCU_override) { |
| std::wstring temp(g_kRegPathHKCU); |
| - temp.append(nt::HKCU_override); |
| + temp.append(g_HKCU_override); |
| temp.append(L"\\"); |
| - ::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1); |
| + ::wcsncpy(g_override_path, temp.c_str(), nt::g_kRegMaxPathLen); |
| g_reg_redirection = true; |
| return g_override_path; |
| - } else if ((key == nt::HKLM) && (::wcslen(nt::HKLM_override) != 0)) { |
| + } else if (key == nt::HKLM && *g_HKLM_override) { |
| + // Yes, HKLM override goes into HKCU. This is not a typo. |
| std::wstring temp(g_kRegPathHKCU); |
| - temp.append(nt::HKLM_override); |
| + temp.append(g_HKLM_override); |
| temp.append(L"\\"); |
| - ::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1); |
| + ::wcsncpy(g_override_path, temp.c_str(), nt::g_kRegMaxPathLen); |
| g_reg_redirection = true; |
| return g_override_path; |
| } |
| @@ -136,14 +465,36 @@ const wchar_t* ConvertRootKey(nt::ROOT_KEY root) { |
| return g_kRegPathHKLM; |
| } |
| +// This utility should be called on an externally provided subkey path. |
| +// - Ensures there are no starting or trailing backslashes. |
| +// - Note from MSDN: "Key names cannot include the backslash character (\), |
| +// but any other printable character can be used." |
| +void SanitizeSubkeyPath(std::wstring* input) { |
| + assert(input != nullptr); |
| + |
| + // Remove any trailing backslashes. |
| + while (!input->empty() && input->back() == L'\\') |
| + input->pop_back(); |
| + |
| + // Remove any starting backslashes. |
| + size_t index = 0; |
| + while ((*input)[index] == L'\\') |
| + ++index; |
| + if (!index) |
| + *input = input->substr(index); |
| +} |
| + |
| // 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. |
| +// - |subkey_path| should be passed to SanitizeSubkeyPath() first. |
| // - E.g. base hive: "\Registry\Machine\", "\Registry\User\<SID>\". |
| bool ParseFullRegPath(const wchar_t* converted_root, |
| - const wchar_t* subkey_path, |
| + const std::wstring& subkey_path, |
| std::wstring* out_base, |
| std::vector<std::wstring>* subkeys) { |
| + assert(converted_root != nullptr); |
| + |
| out_base->clear(); |
| subkeys->clear(); |
| std::wstring temp = L""; |
| @@ -157,8 +508,7 @@ bool ParseFullRegPath(const wchar_t* converted_root, |
| // |subkey_path| = "SOFTWARE\Google\Chrome\BrowserSec". |
| temp.append(converted_root); |
| } |
| - if (subkey_path != nullptr) |
| - temp.append(subkey_path); |
| + temp.append(subkey_path); |
| // Tokenize the full path. |
| size_t find_start = 0; |
| @@ -198,6 +548,10 @@ bool ParseFullRegPath(const wchar_t* converted_root, |
| return true; |
| } |
| +//------------------------------------------------------------------------------ |
| +// Misc wrapper functions - LOCAL |
| +//------------------------------------------------------------------------------ |
| + |
| NTSTATUS CreateKeyWrapper(const std::wstring& key_path, |
| ACCESS_MASK access, |
| HANDLE* out_handle, |
| @@ -217,10 +571,6 @@ NTSTATUS CreateKeyWrapper(const std::wstring& key_path, |
| 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 |
| //------------------------------------------------------------------------------ |
| @@ -232,10 +582,23 @@ bool CreateRegKey(ROOT_KEY root, |
| if (!g_initialized) |
| InitNativeRegApi(); |
| + // |key_path| can be null or empty, but it can't be longer than |
| + // |g_kRegMaxPathLen| at this point. |
| + if (key_path != nullptr && |
| + ::wcsnlen(key_path, g_kRegMaxPathLen + 1) == g_kRegMaxPathLen + 1) |
| + return false; |
| + |
| + std::wstring redirected_key_path; |
| + if (key_path) { |
| + redirected_key_path = key_path; |
| + SanitizeSubkeyPath(&redirected_key_path); |
| + ProcessRedirection(root, access, &redirected_key_path); |
| + } |
| + |
| std::wstring current_path; |
| std::vector<std::wstring> subkeys; |
| - if (!ParseFullRegPath(ConvertRootKey(root), key_path, ¤t_path, |
| - &subkeys)) |
| + if (!ParseFullRegPath(ConvertRootKey(root), redirected_key_path, |
| + ¤t_path, &subkeys)) |
| return false; |
| // Open the base hive first. It should always exist already. |
| @@ -307,13 +670,24 @@ bool OpenRegKey(ROOT_KEY root, |
| if (!g_initialized) |
| InitNativeRegApi(); |
| + // |key_path| can be null or empty, but it can't be longer than |
| + // |g_kRegMaxPathLen| at this point. |
| + if (key_path != nullptr && |
| + ::wcsnlen(key_path, g_kRegMaxPathLen + 1) == g_kRegMaxPathLen + 1) |
| + return false; |
| + |
| 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); |
| + std::wstring full_path; |
| + if (key_path) { |
| + full_path = key_path; |
| + SanitizeSubkeyPath(&full_path); |
| + ProcessRedirection(root, access, &full_path); |
| + } |
| + full_path.insert(0, ConvertRootKey(root)); |
| g_rtl_init_unicode_string(&key_path_uni, full_path.c_str()); |
| InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL, |
| @@ -345,10 +719,12 @@ bool DeleteRegKey(HANDLE key) { |
| } |
| // wrapper function |
| -bool DeleteRegKey(ROOT_KEY root, const wchar_t* key_path) { |
| +bool DeleteRegKey(ROOT_KEY root, |
| + WOW64_OVERRIDE wow64_override, |
| + const wchar_t* key_path) { |
| HANDLE key = INVALID_HANDLE_VALUE; |
| - if (!OpenRegKey(root, key_path, DELETE, &key, nullptr)) |
| + if (!OpenRegKey(root, key_path, DELETE | wow64_override, &key, nullptr)) |
| return false; |
| if (!DeleteRegKey(key)) { |
| @@ -378,6 +754,10 @@ bool QueryRegKeyValue(HANDLE key, |
| if (!g_initialized) |
| InitNativeRegApi(); |
| + if (value_name == nullptr || *value_name == L'\0' || |
|
grt (UTC plus 2)
2016/09/30 09:32:16
will g_nt_query_value_key already do this validati
penny
2016/10/01 01:44:25
Right, so yes, the ntdll functions should fail out
grt (UTC plus 2)
2016/10/02 20:10:54
Seems okay to me. Thanks.
|
| + ::wcsnlen(value_name, g_kRegMaxValueName) == g_kRegMaxValueName) |
| + return false; |
| + |
| NTSTATUS ntstatus = STATUS_UNSUCCESSFUL; |
| UNICODE_STRING value_uni = {}; |
| g_rtl_init_unicode_string(&value_uni, value_name); |
| @@ -430,13 +810,13 @@ bool QueryRegValueDWORD(HANDLE key, |
| // wrapper function |
| bool QueryRegValueDWORD(ROOT_KEY root, |
| + WOW64_OVERRIDE wow64_override, |
| 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)) |
| + if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL)) |
| return false; |
| if (!QueryRegValueDWORD(key, value_name, out_dword)) { |
| @@ -468,13 +848,13 @@ bool QueryRegValueSZ(HANDLE key, |
| // wrapper function |
| bool QueryRegValueSZ(ROOT_KEY root, |
| + WOW64_OVERRIDE wow64_override, |
| 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)) |
| + if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL)) |
| return false; |
| if (!QueryRegValueSZ(key, value_name, out_sz)) { |
| @@ -522,13 +902,13 @@ bool QueryRegValueMULTISZ(HANDLE key, |
| // wrapper function |
| bool QueryRegValueMULTISZ(ROOT_KEY root, |
| + WOW64_OVERRIDE wow64_override, |
| 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)) |
| + if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL)) |
| return false; |
| if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) { |
| @@ -552,6 +932,10 @@ bool SetRegKeyValue(HANDLE key, |
| if (!g_initialized) |
| InitNativeRegApi(); |
| + if (value_name == nullptr || *value_name == L'\0' || |
| + ::wcsnlen(value_name, g_kRegMaxValueName) == g_kRegMaxValueName) |
| + return false; |
| + |
| NTSTATUS ntstatus = STATUS_UNSUCCESSFUL; |
| UNICODE_STRING value_uni = {}; |
| g_rtl_init_unicode_string(&value_uni, value_name); |
| @@ -574,12 +958,13 @@ bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) { |
| // wrapper function |
| bool SetRegValueDWORD(ROOT_KEY root, |
| + WOW64_OVERRIDE wow64_override, |
| 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)) |
| + if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL)) |
| return false; |
| if (!SetRegValueDWORD(key, value_name, value)) { |
| @@ -606,12 +991,13 @@ bool SetRegValueSZ(HANDLE key, |
| // wrapper function |
| bool SetRegValueSZ(ROOT_KEY root, |
| + WOW64_OVERRIDE wow64_override, |
| 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)) |
| + if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL)) |
| return false; |
| if (!SetRegValueSZ(key, value_name, value)) { |
| @@ -655,12 +1041,13 @@ bool SetRegValueMULTISZ(HANDLE key, |
| // wrapper function |
| bool SetRegValueMULTISZ(ROOT_KEY root, |
| + WOW64_OVERRIDE wow64_override, |
| 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)) |
| + if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL)) |
| return false; |
| if (!SetRegValueMULTISZ(key, value_name, values)) { |
| @@ -682,4 +1069,49 @@ const wchar_t* GetCurrentUserSidString() { |
| return g_current_user_sid_string; |
| } |
| +bool IsCurrentProcWow64() { |
| + if (!g_initialized) |
| + InitNativeRegApi(); |
| + |
| + return g_wow64_proc; |
| +} |
| + |
| +bool SetTestingOverride(nt::ROOT_KEY root, const std::wstring& new_path) { |
|
grt (UTC plus 2)
2016/09/30 09:32:16
nit: omit "nt::" here and below since this is with
penny
2016/10/01 01:44:25
Done.
|
| + if (!g_initialized) |
| + InitNativeRegApi(); |
| + |
| + std::wstring sani_new_path = new_path; |
| + SanitizeSubkeyPath(&sani_new_path); |
| + if (::wcsnlen(sani_new_path.c_str(), nt::g_kRegMaxPathLen + 1) == |
|
grt (UTC plus 2)
2016/09/30 09:32:16
why not sani_new_path.length() > g_kRegMaxPathLen?
penny
2016/10/01 01:44:25
Thank you. And good question.
|
| + nt::g_kRegMaxPathLen + 1) |
| + return false; |
| + |
| + // Convert nt::AUTO to the appropriate root key. |
| + nt::ROOT_KEY temp_root = root; |
| + if (root == nt::AUTO) |
| + temp_root = g_system_install ? nt::HKLM : nt::HKCU; |
| + |
| + if (temp_root == nt::HKCU) |
|
grt (UTC plus 2)
2016/09/30 09:32:16
how about doing away with temp_root and changing t
penny
2016/10/01 01:44:25
You know, for some reason I was thinking |root| ar
|
| + ::wcsncpy(g_HKCU_override, sani_new_path.c_str(), nt::g_kRegMaxPathLen); |
| + else |
| + ::wcsncpy(g_HKLM_override, sani_new_path.c_str(), nt::g_kRegMaxPathLen); |
| + |
| + return true; |
| +} |
| + |
| +void GetTestingOverride(nt::ROOT_KEY root, std::wstring* out_path) { |
| + if (!g_initialized) |
| + InitNativeRegApi(); |
| + |
| + // Convert nt::AUTO to the appropriate root key. |
| + nt::ROOT_KEY temp_root = root; |
| + if (root == nt::AUTO) |
| + temp_root = g_system_install ? nt::HKLM : nt::HKCU; |
| + |
| + if (temp_root == nt::HKCU) |
| + out_path->assign(g_HKCU_override); |
| + else |
| + out_path->assign(g_HKLM_override); |
| +} |
| + |
| }; // namespace nt |