[M54 Merge] NTRegistry - added wow64 redirection support.

TESTS=chrome_elf_unittests: NtRegistryTest*
BUG=641169
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.win:win10_chromium_x64_rel_ng

Review-Url: https://codereview.chromium.org/2345913003
Cr-Commit-Position: refs/heads/master@{#422919}
(cherry picked from commit 5379f1724fc3c193828e137faba13c8d59c3f228)

Review URL: https://codereview.chromium.org/2408923002 .

Cr-Commit-Position: refs/branch-heads/2840@{#707}
Cr-Branched-From: 1ae106dbab4bddd85132d5b75c670794311f4c57-refs/heads/master@{#414607}
diff --git a/chrome/install_static/BUILD.gn b/chrome/install_static/BUILD.gn
index a58ede4..4804e26 100644
--- a/chrome/install_static/BUILD.gn
+++ b/chrome/install_static/BUILD.gn
@@ -11,7 +11,7 @@
 # Please don't add dependencies on other system libraries.
 static_library("install_static_util") {
   public_deps = [
-    "//chrome_elf/nt_registry:nt_registry",
+    "//chrome_elf:nt_registry",
   ]
 
   sources = [
diff --git a/chrome/install_static/install_util.cc b/chrome/install_static/install_util.cc
index 3d492745..da7e336 100644
--- a/chrome/install_static/install_util.cc
+++ b/chrome/install_static/install_util.cc
@@ -250,10 +250,9 @@
 
 bool GetCollectStatsConsentImpl(const std::wstring& exe_path) {
   bool enabled = true;
-  bool controlled_by_policy = ReportingIsEnforcedByPolicy(&enabled);
 
-  if (controlled_by_policy && !enabled)
-    return false;
+  if (ReportingIsEnforcedByPolicy(&enabled))
+    return enabled;
 
   bool system_install = IsSystemInstall(exe_path.c_str());
   std::wstring app_guid;
@@ -271,8 +270,9 @@
   std::wstring full_key_path(kRegPathClientStateMedium);
   full_key_path.append(1, L'\\');
   full_key_path.append(app_guid);
-  if (system_install && nt::QueryRegValueDWORD(nt::HKLM, full_key_path.c_str(),
-                                               kRegValueUsageStats, &out_value))
+  if (system_install &&
+      nt::QueryRegValueDWORD(nt::HKLM, nt::WOW6432, full_key_path.c_str(),
+                             kRegValueUsageStats, &out_value))
     return (out_value == 1);
 
   // Second, try kRegPathClientState.
@@ -280,8 +280,8 @@
   full_key_path.append(1, L'\\');
   full_key_path.append(app_guid);
   return (nt::QueryRegValueDWORD((system_install ? nt::HKLM : nt::HKCU),
-                                 full_key_path.c_str(), kRegValueUsageStats,
-                                 &out_value) &&
+                                 nt::WOW6432, full_key_path.c_str(),
+                                 kRegValueUsageStats, &out_value) &&
           out_value == 1);
 }
 
@@ -411,8 +411,8 @@
   full_key_path.append(1, L'\\');
   full_key_path.append(kAppGuidGoogleChrome);
   if (!nt::QueryRegValueSZ((is_system_install ? nt::HKLM : nt::HKCU),
-                           full_key_path.c_str(), kUninstallArgumentsField,
-                           &args))
+                           nt::WOW6432, full_key_path.c_str(),
+                           kUninstallArgumentsField, &args))
     return false;
 
   return (args.find(L"--multi-install") != std::wstring::npos);
@@ -430,7 +430,7 @@
   std::wstring registry_path = GetChromeInstallRegistryPath();
 
   DWORD out_value = 0;
-  if (!nt::QueryRegValueDWORD(nt::HKCU, registry_path.c_str(),
+  if (!nt::QueryRegValueDWORD(nt::HKCU, nt::WOW6432, registry_path.c_str(),
                               kRegValueChromeStatsSample, &out_value)) {
     // If reading the value failed, treat it as though sampling isn't in effect,
     // implicitly meaning this install is in the sample.
@@ -443,8 +443,8 @@
   std::wstring registry_path = GetChromeInstallRegistryPath();
 
   HANDLE key_handle = INVALID_HANDLE_VALUE;
-  if (!nt::CreateRegKey(nt::HKCU, registry_path.c_str(), KEY_SET_VALUE,
-                        &key_handle)) {
+  if (!nt::CreateRegKey(nt::HKCU, registry_path.c_str(),
+                        KEY_SET_VALUE | KEY_WOW64_32KEY, &key_handle)) {
     nt::CloseRegKey(key_handle);
     return false;
   }
@@ -457,14 +457,14 @@
   DWORD value = 0;
 
   // First, try HKLM.
-  if (nt::QueryRegValueDWORD(nt::HKLM, kRegPathChromePolicy,
+  if (nt::QueryRegValueDWORD(nt::HKLM, nt::NONE, kRegPathChromePolicy,
                              kMetricsReportingEnabled, &value)) {
     *metrics_is_enforced_by_policy = (value != 0);
     return true;
   }
 
   // Second, try HKCU.
-  if (nt::QueryRegValueDWORD(nt::HKCU, kRegPathChromePolicy,
+  if (nt::QueryRegValueDWORD(nt::HKCU, nt::NONE, kRegPathChromePolicy,
                              kMetricsReportingEnabled, &value)) {
     *metrics_is_enforced_by_policy = (value != 0);
     return true;
@@ -640,16 +640,16 @@
       std::wstring full_key_path(kRegPathClientState);
       full_key_path.append(1, L'\\');
       full_key_path.append(kAppGuidGoogleBinaries);
-      channel_available =
-          nt::QueryRegValueSZ(is_per_user_install ? nt::HKCU : nt::HKLM,
-                              full_key_path.c_str(), kRegApField, &value);
+      channel_available = nt::QueryRegValueSZ(
+          is_per_user_install ? nt::HKCU : nt::HKLM, nt::WOW6432,
+          full_key_path.c_str(), kRegApField, &value);
     } else {
       std::wstring full_key_path(kRegPathClientState);
       full_key_path.append(1, L'\\');
       full_key_path.append(kAppGuidGoogleChrome);
-      channel_available =
-          nt::QueryRegValueSZ(is_per_user_install ? nt::HKCU : nt::HKLM,
-                              full_key_path.c_str(), kRegApField, &value);
+      channel_available = nt::QueryRegValueSZ(
+          is_per_user_install ? nt::HKCU : nt::HKLM, nt::WOW6432,
+          full_key_path.c_str(), kRegApField, &value);
     }
     if (channel_available) {
       static const wchar_t kChromeChannelBetaPattern[] = L"1?1-*";
@@ -698,7 +698,7 @@
   // Consider whether Chromium should connect to Google update to manage
   // updates. Should this be returning an empty string for Chromium builds?.
   std::wstring update_version;
-  if (nt::QueryRegValueSZ(nt::AUTO, kRegPathGoogleUpdate,
+  if (nt::QueryRegValueSZ(nt::AUTO, nt::WOW6432, kRegPathGoogleUpdate,
                           kRegGoogleUpdateVersion, &update_version))
     return UTF16ToUTF8(update_version);
 
diff --git a/chrome_elf/BUILD.gn b/chrome_elf/BUILD.gn
index 5cc27bd..1fb0102 100644
--- a/chrome_elf/BUILD.gn
+++ b/chrome_elf/BUILD.gn
@@ -47,10 +47,10 @@
     ":chrome_elf_security",
     ":constants",
     ":hook_util",
+    ":nt_registry",
     "//base",
     "//build/config/sanitizers:deps",
     "//chrome/install_static:install_static_util",
-    "//chrome_elf/nt_registry:nt_registry",
     "//components/crash/content/app",
     "//components/crash/core/common",
     "//content/public/common:result_codes",
@@ -87,7 +87,7 @@
   ]
   deps = [
     ":constants",
-    "//chrome_elf/nt_registry:nt_registry",
+    ":nt_registry",
   ]
 }
 
@@ -138,8 +138,8 @@
   deps = [
     ":constants",
     ":hook_util",
+    ":nt_registry",
     "//chrome/install_static:install_static_util",
-    "//chrome_elf/nt_registry:nt_registry",
 
     # Still uses base/win/pe_image.h
     "//base",
@@ -157,6 +157,18 @@
   ]
 }
 
+# This target contains utility functions which must only depend on
+# kernel32. Please don't add dependencies on other system libraries.
+static_library("nt_registry") {
+  sources = [
+    "../sandbox/win/src/nt_internals.h",
+    "nt_registry/nt_registry.cc",
+    "nt_registry/nt_registry.h",
+  ]
+
+  libs = [ "kernel32.lib" ]
+}
+
 ##------------------------------------------------------------------------------
 ## tests
 ##------------------------------------------------------------------------------
@@ -167,6 +179,7 @@
     "blacklist/test/blacklist_test.cc",
     "chrome_elf_util_unittest.cc",
     "elf_imports_unittest.cc",
+    "nt_registry/nt_registry_unittest.cc",
     "run_all_unittests.cc",
   ]
   include_dirs = [ "$target_gen_dir" ]
@@ -176,12 +189,12 @@
     ":chrome_elf_security",
     ":constants",
     ":hook_util",
+    ":nt_registry",
     "//base",
     "//base/test:test_support",
     "//chrome",
     "//chrome/common:version_header",
     "//chrome/install_static:install_static_util",
-    "//chrome_elf/nt_registry:nt_registry",
     "//sandbox",
     "//testing/gtest",
   ]
@@ -220,10 +233,10 @@
   ]
   deps = [
     ":blacklist",
+    ":nt_registry",
     "//base",
     "//build/config/sanitizers:deps",
     "//chrome/install_static:install_static_util",
-    "//chrome_elf/nt_registry:nt_registry",
   ]
   ldflags = [
     "/NODEFAULTLIB:user32.lib",
diff --git a/chrome_elf/blacklist/test/blacklist_test.cc b/chrome_elf/blacklist/test/blacklist_test.cc
index b970057..16ce917 100644
--- a/chrome_elf/blacklist/test/blacklist_test.cc
+++ b/chrome_elf/blacklist/test/blacklist_test.cc
@@ -133,20 +133,18 @@
   // process-specific environment variables, for our test DLLs to access.
   // This will only work as long as the IPC is within the same process.
   void IpcOverrides() {
-    if (::wcslen(nt::HKCU_override) != 0) {
-      ASSERT_TRUE(
-          ::SetEnvironmentVariableW(L"hkcu_override", nt::HKCU_override));
-    }
-    if (::wcslen(nt::HKLM_override) != 0) {
-      ASSERT_TRUE(
-          ::SetEnvironmentVariableW(L"hklm_override", nt::HKLM_override));
-    }
+    base::string16 temp = nt::GetTestingOverride(nt::HKCU);
+    if (!temp.empty())
+      ASSERT_TRUE(::SetEnvironmentVariableW(L"hkcu_override", temp.c_str()));
+    temp = nt::GetTestingOverride(nt::HKLM);
+    if (!temp.empty())
+      ASSERT_TRUE(::SetEnvironmentVariableW(L"hklm_override", temp.c_str()));
   }
 
   void SetUp() override {
     base::string16 temp;
     override_manager_.OverrideRegistry(HKEY_CURRENT_USER, &temp);
-    ::wcsncpy(nt::HKCU_override, temp.c_str(), nt::g_kRegMaxPathLen - 1);
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKCU, temp));
 
     // Make the override path available to our test DLL.
     IpcOverrides();
@@ -203,10 +201,9 @@
     TestDll_RemoveDllFromBlacklist(kTestDllName1);
     TestDll_RemoveDllFromBlacklist(kTestDllName2);
     TestDll_RemoveDllFromBlacklist(kTestDllName3);
-  }
 
-  // A scoped temporary directory to be destroyed with this test.
-  base::ScopedTempDir reg_override_dir_;
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKCU, base::string16()));
+  }
 };
 
 TEST_F(BlacklistTest, Beacon) {
diff --git a/chrome_elf/blacklist/test/blacklist_test_main_dll.cc b/chrome_elf/blacklist/test/blacklist_test_main_dll.cc
index 3b2c4d7..5f919f7 100644
--- a/chrome_elf/blacklist/test/blacklist_test_main_dll.cc
+++ b/chrome_elf/blacklist/test/blacklist_test_main_dll.cc
@@ -14,22 +14,20 @@
 void GetIpcOverrides() {
   DWORD buffer_size = ::GetEnvironmentVariableW(L"hkcu_override", nullptr, 0);
   if (buffer_size > 0) {
-    wchar_t* content = new wchar_t[buffer_size];
+    std::wstring content(buffer_size, L'\0');
     buffer_size =
-        ::GetEnvironmentVariableW(L"hkcu_override", content, buffer_size);
+        ::GetEnvironmentVariableW(L"hkcu_override", &content[0], buffer_size);
     if (buffer_size)
-      ::wcsncpy(nt::HKCU_override, content, nt::g_kRegMaxPathLen - 1);
-    delete[] content;
+      nt::SetTestingOverride(nt::HKCU, content);
   }
 
   buffer_size = ::GetEnvironmentVariableW(L"hklm_override", nullptr, 0);
   if (buffer_size > 0) {
-    wchar_t* content = new wchar_t[buffer_size];
+    std::wstring content(buffer_size, L'\0');
     buffer_size =
-        ::GetEnvironmentVariableW(L"hklm_override", content, buffer_size);
+        ::GetEnvironmentVariableW(L"hklm_override", &content[0], buffer_size);
     if (buffer_size)
-      ::wcsncpy(nt::HKLM_override, content, nt::g_kRegMaxPathLen - 1);
-    delete[] content;
+      nt::SetTestingOverride(nt::HKLM, content);
   }
 
   return;
diff --git a/chrome_elf/chrome_elf_util_unittest.cc b/chrome_elf/chrome_elf_util_unittest.cc
index ad4671a..f24bde7 100644
--- a/chrome_elf/chrome_elf_util_unittest.cc
+++ b/chrome_elf/chrome_elf_util_unittest.cc
@@ -72,17 +72,25 @@
 }
 
 void RegRedirect(nt::ROOT_KEY key,
-                 registry_util::RegistryOverrideManager& rom) {
+                 registry_util::RegistryOverrideManager* rom) {
+  ASSERT_NE(key, nt::AUTO);
   base::string16 temp;
 
   if (key == nt::HKCU) {
-    rom.OverrideRegistry(HKEY_CURRENT_USER, &temp);
-    ::wcsncpy(nt::HKCU_override, temp.c_str(), nt::g_kRegMaxPathLen - 1);
-  } else if (key == nt::HKLM) {
-    rom.OverrideRegistry(HKEY_LOCAL_MACHINE, &temp);
-    ::wcsncpy(nt::HKLM_override, temp.c_str(), nt::g_kRegMaxPathLen - 1);
+    rom->OverrideRegistry(HKEY_CURRENT_USER, &temp);
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKCU, temp));
+  } else {
+    rom->OverrideRegistry(HKEY_LOCAL_MACHINE, &temp);
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKLM, temp));
   }
-  // nt::AUTO should not be passed into this function.
+}
+
+void CancelRegRedirect(nt::ROOT_KEY key) {
+  ASSERT_NE(key, nt::AUTO);
+  if (key == nt::HKCU)
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKCU, base::string16()));
+  else
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKLM, base::string16()));
 }
 
 TEST(ChromeElfUtilTest, CanaryTest) {
@@ -108,7 +116,7 @@
 
   // Set up registry override for this test.
   registry_util::RegistryOverrideManager override_manager;
-  RegRedirect(nt::HKCU, override_manager);
+  RegRedirect(nt::HKCU, &override_manager);
 
   // First, ensure that the emergency-off finch signal works.
   EXPECT_TRUE(SetSecurityFinchFlag(true));
@@ -119,112 +127,8 @@
   // Second, test that the process mitigation is set when no finch signal.
   EarlyBrowserSecurity();
   EXPECT_TRUE(IsSecuritySet());
-}
 
-//------------------------------------------------------------------------------
-// NT registry API tests (chrome_elf_reg)
-//------------------------------------------------------------------------------
-
-TEST(ChromeElfUtilTest, NTRegistry) {
-  HANDLE key_handle;
-  const wchar_t* dword_val_name = L"DwordTestValue";
-  DWORD dword_val = 1234;
-  const wchar_t* sz_val_name = L"SzTestValue";
-  base::string16 sz_val = L"blah de blah de blahhhhh.";
-  const wchar_t* sz_val_name2 = L"SzTestValueEmpty";
-  base::string16 sz_val2 = L"";
-  const wchar_t* multisz_val_name = L"SzmultiTestValue";
-  std::vector<base::string16> multisz_val;
-  base::string16 multi1 = L"one";
-  base::string16 multi2 = L"two";
-  base::string16 multi3 = L"three";
-  const wchar_t* multisz_val_name2 = L"SzmultiTestValueBad";
-  base::string16 multi_empty = L"";
-  const wchar_t* sz_new_key_1 = L"test\\new\\subkey";
-  const wchar_t* sz_new_key_2 = L"test\\new\\subkey\\blah\\";
-  const wchar_t* sz_new_key_3 = L"\\test\\new\\subkey\\\\blah2";
-
-  // Set up registry override for this test.
-  registry_util::RegistryOverrideManager override_manager;
-  RegRedirect(nt::HKCU, override_manager);
-
-  // Create a temp key to play under.
-  ASSERT_TRUE(nt::CreateRegKey(nt::HKCU, elf_sec::kRegSecurityPath,
-                               KEY_ALL_ACCESS, &key_handle));
-
-  // Exercise the supported getter & setter functions.
-  EXPECT_TRUE(nt::SetRegValueDWORD(key_handle, dword_val_name, dword_val));
-  EXPECT_TRUE(nt::SetRegValueSZ(key_handle, sz_val_name, sz_val));
-  EXPECT_TRUE(nt::SetRegValueSZ(key_handle, sz_val_name2, sz_val2));
-
-  DWORD get_dword = 0;
-  base::string16 get_sz;
-  EXPECT_TRUE(nt::QueryRegValueDWORD(key_handle, dword_val_name, &get_dword) &&
-              get_dword == dword_val);
-  EXPECT_TRUE(nt::QueryRegValueSZ(key_handle, sz_val_name, &get_sz) &&
-              get_sz.compare(sz_val) == 0);
-  EXPECT_TRUE(nt::QueryRegValueSZ(key_handle, sz_val_name2, &get_sz) &&
-              get_sz.compare(sz_val2) == 0);
-
-  multisz_val.push_back(multi1);
-  multisz_val.push_back(multi2);
-  multisz_val.push_back(multi3);
-  EXPECT_TRUE(
-      nt::SetRegValueMULTISZ(key_handle, multisz_val_name, multisz_val));
-  multisz_val.clear();
-  multisz_val.push_back(multi_empty);
-  EXPECT_TRUE(
-      nt::SetRegValueMULTISZ(key_handle, multisz_val_name2, multisz_val));
-  multisz_val.clear();
-
-  EXPECT_TRUE(
-      nt::QueryRegValueMULTISZ(key_handle, multisz_val_name, &multisz_val));
-  if (multisz_val.size() == 3) {
-    EXPECT_TRUE(multi1.compare(multisz_val.at(0)) == 0);
-    EXPECT_TRUE(multi2.compare(multisz_val.at(1)) == 0);
-    EXPECT_TRUE(multi3.compare(multisz_val.at(2)) == 0);
-  } else {
-    EXPECT_TRUE(false);
-  }
-  multisz_val.clear();
-
-  EXPECT_TRUE(
-      nt::QueryRegValueMULTISZ(key_handle, multisz_val_name2, &multisz_val));
-  if (multisz_val.size() == 1) {
-    EXPECT_TRUE(multi_empty.compare(multisz_val.at(0)) == 0);
-  } else {
-    EXPECT_TRUE(false);
-  }
-  multisz_val.clear();
-
-  // Clean up
-  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
-  nt::CloseRegKey(key_handle);
-
-  // More tests for CreateRegKey recursion.
-  ASSERT_TRUE(
-      nt::CreateRegKey(nt::HKCU, sz_new_key_1, KEY_ALL_ACCESS, nullptr));
-  EXPECT_TRUE(nt::OpenRegKey(nt::HKCU, sz_new_key_1, KEY_ALL_ACCESS,
-                             &key_handle, nullptr));
-  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
-  nt::CloseRegKey(key_handle);
-
-  ASSERT_TRUE(
-      nt::CreateRegKey(nt::HKCU, sz_new_key_2, KEY_ALL_ACCESS, nullptr));
-  EXPECT_TRUE(nt::OpenRegKey(nt::HKCU, sz_new_key_2, KEY_ALL_ACCESS,
-                             &key_handle, nullptr));
-  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
-  nt::CloseRegKey(key_handle);
-
-  ASSERT_TRUE(
-      nt::CreateRegKey(nt::HKCU, sz_new_key_3, KEY_ALL_ACCESS, nullptr));
-  EXPECT_TRUE(nt::OpenRegKey(nt::HKCU, L"test\\new\\subkey\\blah2",
-                             KEY_ALL_ACCESS, &key_handle, nullptr));
-  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
-  nt::CloseRegKey(key_handle);
-
-  ASSERT_TRUE(nt::CreateRegKey(nt::HKCU, nullptr, KEY_ALL_ACCESS, &key_handle));
-  nt::CloseRegKey(key_handle);
+  CancelRegRedirect(nt::HKCU);
 }
 
 // Parameterized test with paramters:
@@ -237,8 +141,8 @@
  protected:
   void SetUp() override {
     // Set up registry override for these tests.
-    RegRedirect(nt::HKLM, override_manager_);
-    RegRedirect(nt::HKCU, override_manager_);
+    RegRedirect(nt::HKLM, &override_manager_);
+    RegRedirect(nt::HKCU, &override_manager_);
 
     const char* app;
     const char* level;
@@ -263,6 +167,11 @@
     }
   }
 
+  void TearDown() override {
+    CancelRegRedirect(nt::HKCU);
+    CancelRegRedirect(nt::HKLM);
+  }
+
   base::string16 BuildKey(const wchar_t* path, const wchar_t* guid) {
     base::string16 full_key_path(path);
     full_key_path.append(1, L'\\');
diff --git a/chrome_elf/nt_registry/BUILD.gn b/chrome_elf/nt_registry/BUILD.gn
deleted file mode 100644
index e58bac7..0000000
--- a/chrome_elf/nt_registry/BUILD.gn
+++ /dev/null
@@ -1,20 +0,0 @@
-# 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.
-
-import("//build/config/chrome_build.gni")
-import("//testing/test.gni")
-
-assert(is_win)
-
-# This target only contains utility functions which must only depend on
-# kernel32. Please don't add dependencies on other system libraries.
-static_library("nt_registry") {
-  sources = [
-    "../../sandbox/win/src/nt_internals.h",
-    "nt_registry.cc",
-    "nt_registry.h",
-  ]
-
-  libs = [ "kernel32.lib" ]
-}
diff --git a/chrome_elf/nt_registry/DEPS b/chrome_elf/nt_registry/DEPS
index e54eb13..066423b 100644
--- a/chrome_elf/nt_registry/DEPS
+++ b/chrome_elf/nt_registry/DEPS
@@ -6,3 +6,8 @@
   "-chrome_elf",
   "+chrome_elf/nt_registry/nt_registry.h",
 ]
+specific_include_rules = {
+  ".*_unittest\.cc": [
+    "+base",
+  ]
+}
\ No newline at end of file
diff --git a/chrome_elf/nt_registry/nt_registry.cc b/chrome_elf/nt_registry/nt_registry.cc
index 373f009..4c65af8 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,20 @@
 // 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;
+bool g_wow64_proc = false;
 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"";
 
-// Not using install_util, to prevent circular dependency.
+// 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_static, to prevent circular dependency.
 bool IsThisProcSystem() {
   wchar_t program_dir[MAX_PATH] = {};
   wchar_t* cmd_line = GetCommandLineW();
@@ -42,6 +51,22 @@
   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,96 +114,415 @@
     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(&current_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;
 }
 
-const wchar_t* ConvertRootKey(nt::ROOT_KEY root) {
-  nt::ROOT_KEY key = root;
+//------------------------------------------------------------------------------
+// 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.
+//------------------------------------------------------------------------------
 
-  if (!root) {
-    // AUTO
-    key = g_system_install ? nt::HKLM : nt::HKCU;
+// 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'\\');
+  assert(root != nt::AUTO);
+
+  // |subkey_path| could legitimately be empty.
+  if (subkey_path->empty() ||
+      (access & KEY_WOW64_32KEY && access & KEY_WOW64_64KEY))
+    return;
+
+  // 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 (root == nt::HKCU ? *g_HKCU_override : *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 = (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)) {
+        // 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 ((key == nt::HKCU) && (::wcslen(nt::HKCU_override) != 0)) {
-    std::wstring temp(g_kRegPathHKCU);
-    temp.append(nt::HKCU_override);
+  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
+//------------------------------------------------------------------------------
+
+std::wstring ConvertRootKey(nt::ROOT_KEY root) {
+  assert(root != nt::AUTO);
+
+  if (root == nt::HKCU && *g_HKCU_override) {
+    std::wstring temp = g_kRegPathHKCU;
+    temp.append(g_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);
+    return temp;
+  } else if (root == nt::HKLM && *g_HKLM_override) {
+    // Yes, HKLM override goes into HKCU.  This is not a typo.
+    std::wstring temp = g_kRegPathHKCU;
+    temp.append(g_HKLM_override);
     temp.append(L"\\");
-    ::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1);
-    g_reg_redirection = true;
-    return g_override_path;
+    return temp;
   }
 
-  g_reg_redirection = false;
-  if (key == nt::HKCU)
-    return g_kRegPathHKCU;
-  else
-    return g_kRegPathHKLM;
+  return (root == nt::HKCU) ? g_kRegPathHKCU : g_kRegPathHKLM;
+}
+
+// This utility should be called on an externally provided subkey path.
+// - Ensures there are no starting or trailing backslashes, and no more than
+// - one backslash in a row.
+// - Note from MSDN: "Key names cannot include the backslash character (\),
+//   but any other printable character can be used.  Value names and data can
+//   include the backslash character."
+void SanitizeSubkeyPath(std::wstring* input) {
+  assert(input != nullptr);
+
+  // Remove trailing backslashes.
+  size_t last_valid_pos = input->find_last_not_of(L'\\');
+  if (last_valid_pos == std::wstring::npos) {
+    // The string is all backslashes, or it's empty.  Clear and abort.
+    input->clear();
+    return;
+  }
+  // Chop off the trailing backslashes.
+  input->resize(last_valid_pos + 1);
+
+  // Remove leading backslashes.
+  input->erase(0, input->find_first_not_of(L'\\'));
+
+  // Replace any occurances of more than 1 backslash in a row with just 1.
+  size_t index = input->find_first_of(L"\\");
+  while (index != std::wstring::npos) {
+    // Remove a second consecutive backslash, and leave index where it is,
+    // or move to the next backslash in the string.
+    if ((*input)[index + 1] == L'\\')
+      input->erase(index + 1, 1);
+    else
+      index = input->find_first_of(L"\\", index + 1);
+  }
 }
 
 // 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,
+bool ParseFullRegPath(const std::wstring& converted_root,
+                      const std::wstring& subkey_path,
                       std::wstring* out_base,
                       std::vector<std::wstring>* subkeys) {
   out_base->clear();
   subkeys->clear();
-  std::wstring temp = L"";
+  std::wstring temp_path;
 
-  if (g_reg_redirection) {
+  // Special case if there is testing redirection set up.
+  if (*g_HKCU_override || *g_HKLM_override) {
     // 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);
+    //
+    // Note: bypassing the starting backslash in the |converted_root|.
+    temp_path.append(converted_root, 1, converted_root.size() - 1);
   }
-  if (subkey_path != nullptr)
-    temp.append(subkey_path);
+  temp_path.append(subkey_path);
 
   // Tokenize the full path.
   size_t find_start = 0;
-  size_t delimiter = temp.find_first_of(L'\\');
+  size_t delimiter = temp_path.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);
+    subkeys->emplace_back(temp_path, find_start, delimiter - find_start);
+    // Move past the backslash.
     find_start = delimiter + 1;
-    delimiter = temp.find_first_of(L'\\', find_start);
+    delimiter = temp_path.find_first_of(L'\\', find_start);
   }
-  if (!temp.empty() && find_start < temp.length())
-    // Get the last token.
-    subkeys->push_back(temp.substr(find_start));
+  // Get the last token if there is one.
+  if (!temp_path.empty())
+    subkeys->emplace_back(temp_path, find_start);
 
-  if (g_reg_redirection) {
+  // Special case if there is testing redirection set up.
+  if (*g_HKCU_override || *g_HKLM_override) {
     // 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)))
+    if (0 == temp_path.compare(0, 14, L"REGISTRY\\USER\\"))
       num_base_tokens = 3;
 
     if (subkeys->size() < num_base_tokens)
@@ -186,8 +530,8 @@
 
     // 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());
+    for (size_t i = 0; i < num_base_tokens; ++i) {
+      out_base->append((*subkeys)[i]);
       out_base->push_back(L'\\');
     }
     subkeys->erase(subkeys->begin(), subkeys->begin() + num_base_tokens);
@@ -198,6 +542,10 @@
   return true;
 }
 
+//------------------------------------------------------------------------------
+// Misc wrapper functions - LOCAL
+//------------------------------------------------------------------------------
+
 NTSTATUS CreateKeyWrapper(const std::wstring& key_path,
                           ACCESS_MASK access,
                           HANDLE* out_handle,
@@ -217,10 +565,6 @@
 
 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
 //------------------------------------------------------------------------------
@@ -229,13 +573,29 @@
                   const wchar_t* key_path,
                   ACCESS_MASK access,
                   HANDLE* out_handle OPTIONAL) {
+  // |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;
+
   if (!g_initialized)
     InitNativeRegApi();
 
+  if (root == nt::AUTO)
+    root = g_system_install ? nt::HKLM : nt::HKCU;
+
+  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, &current_path,
-                        &subkeys))
+  if (!ParseFullRegPath(ConvertRootKey(root), redirected_key_path,
+                        &current_path, &subkeys))
     return false;
 
   // Open the base hive first.  It should always exist already.
@@ -304,6 +664,12 @@
                 ACCESS_MASK access,
                 HANDLE* out_handle,
                 NTSTATUS* error_code OPTIONAL) {
+  // |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;
+
   if (!g_initialized)
     InitNativeRegApi();
 
@@ -312,8 +678,16 @@
   OBJECT_ATTRIBUTES obj = {};
   *out_handle = INVALID_HANDLE_VALUE;
 
-  std::wstring full_path(ConvertRootKey(root));
-  full_path.append(key_path);
+  if (root == nt::AUTO)
+    root = g_system_install ? nt::HKLM : nt::HKCU;
+
+  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 @@
 }
 
 // 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)) {
@@ -430,13 +806,13 @@
 
 // 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 +844,13 @@
 
 // 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 +898,13 @@
 
 // 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)) {
@@ -574,12 +950,13 @@
 
 // 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 +983,13 @@
 
 // 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 +1033,13 @@
 
 // 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 +1061,38 @@
   return g_current_user_sid_string;
 }
 
+bool IsCurrentProcWow64() {
+  if (!g_initialized)
+    InitNativeRegApi();
+
+  return g_wow64_proc;
+}
+
+bool SetTestingOverride(ROOT_KEY root, const std::wstring& new_path) {
+  if (!g_initialized)
+    InitNativeRegApi();
+
+  std::wstring sani_new_path = new_path;
+  SanitizeSubkeyPath(&sani_new_path);
+  if (sani_new_path.length() > g_kRegMaxPathLen)
+    return false;
+
+  if (root == HKCU || (root == AUTO && !g_system_install))
+    ::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;
+}
+
+std::wstring GetTestingOverride(ROOT_KEY root) {
+  if (!g_initialized)
+    InitNativeRegApi();
+
+  if (root == HKCU || (root == AUTO && !g_system_install))
+    return g_HKCU_override;
+
+  return g_HKLM_override;
+}
+
 };  // namespace nt
diff --git a/chrome_elf/nt_registry/nt_registry.h b/chrome_elf/nt_registry/nt_registry.h
index 0451fc5..5115410 100644
--- a/chrome_elf/nt_registry/nt_registry.h
+++ b/chrome_elf/nt_registry/nt_registry.h
@@ -28,16 +28,27 @@
 
 namespace nt {
 
-// These globals are only used in test suites that use reg redirection
-// of HKLM and/or HKCU.
-extern const size_t g_kRegMaxPathLen;
-extern wchar_t HKLM_override[];
-extern wchar_t HKCU_override[];
+// Windows registry maximum lengths (in chars).  Not including null char.
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724872(v=vs.85).aspx
+constexpr size_t g_kRegMaxPathLen = 255;
+constexpr size_t g_kRegMaxValueName = 16383;
 
 // AUTO will choose depending on system install or not.
 // Use HKLM or HKCU to override.
 typedef enum _ROOT_KEY { AUTO = 0, HKLM, HKCU } ROOT_KEY;
 
+// Used with wrapper functions to request registry redirection override.
+// Maps to KEY_WOW64_32KEY and KEY_WOW64_64KEY access flags.
+enum WOW64_OVERRIDE {
+  NONE = 0L,
+  WOW6432 = KEY_WOW64_32KEY,
+  WOW6464 = KEY_WOW64_64KEY
+};
+
+//------------------------------------------------------------------------------
+// Create, open, delete, close functions
+//------------------------------------------------------------------------------
+
 // Create and/or open a registry key.
 // - This function will recursively create multiple sub-keys if required for
 //   |key_path|.
@@ -66,7 +77,10 @@
 
 // Delete a registry key.
 // - WRAPPER: Function opens and closes the target key for caller.
-bool DeleteRegKey(ROOT_KEY root, const wchar_t* key_path);
+// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
+bool DeleteRegKey(ROOT_KEY root,
+                  WOW64_OVERRIDE wow64_override,
+                  const wchar_t* key_path);
 
 // Close a registry key handle that was opened with CreateRegKey or OpenRegKey.
 void CloseRegKey(HANDLE key);
@@ -96,7 +110,9 @@
 // Query DWORD value.
 // - WRAPPER: Function opens and closes the target key for caller, and works
 // with DWORD data type.
+// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
 bool QueryRegValueDWORD(ROOT_KEY root,
+                        WOW64_OVERRIDE wow64_override,
                         const wchar_t* key_path,
                         const wchar_t* value_name,
                         DWORD* out_dword);
@@ -112,7 +128,9 @@
 // Query SZ (string) value.
 // - WRAPPER: Function opens and closes the target key for caller, and works
 // with SZ data type.
+// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
 bool QueryRegValueSZ(ROOT_KEY root,
+                     WOW64_OVERRIDE wow64_override,
                      const wchar_t* key_path,
                      const wchar_t* value_name,
                      std::wstring* out_sz);
@@ -128,7 +146,9 @@
 // Query MULTI_SZ (multiple strings) value.
 // - WRAPPER: Function opens and closes the target key for caller, and works
 // with MULTI_SZ data type.
+// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
 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);
@@ -155,7 +175,9 @@
 // Set DWORD value.
 // - WRAPPER: Function opens and closes the target key for caller, and works
 // with DWORD data type.
+// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
 bool SetRegValueDWORD(ROOT_KEY root,
+                      WOW64_OVERRIDE wow64_override,
                       const wchar_t* key_path,
                       const wchar_t* value_name,
                       DWORD value);
@@ -171,7 +193,9 @@
 // Set SZ (string) value.
 // - WRAPPER: Function opens and closes the target key for caller, and works
 // with SZ data type.
+// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
 bool SetRegValueSZ(ROOT_KEY root,
+                   WOW64_OVERRIDE wow64_override,
                    const wchar_t* key_path,
                    const wchar_t* value_name,
                    const std::wstring& value);
@@ -187,7 +211,9 @@
 // Set MULTI_SZ (multiple strings) value.
 // - WRAPPER: Function opens and closes the target key for caller, and works
 // with MULTI_SZ data type.
+// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
 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);
@@ -199,6 +225,15 @@
 // Returns the current user SID in string form.
 const wchar_t* GetCurrentUserSidString();
 
+// Returns true if this process is WOW64.
+bool IsCurrentProcWow64();
+
+// Setter function for test suites that use reg redirection.
+bool SetTestingOverride(ROOT_KEY root, const std::wstring& new_path);
+
+// Getter function for test suites that use reg redirection.
+std::wstring GetTestingOverride(ROOT_KEY root);
+
 };  // namespace nt
 
 #endif  // CHROME_ELF_NT_REGISTRY_NT_REGISTRY_H_
diff --git a/chrome_elf/nt_registry/nt_registry_unittest.cc b/chrome_elf/nt_registry/nt_registry_unittest.cc
new file mode 100644
index 0000000..d202042
--- /dev/null
+++ b/chrome_elf/nt_registry/nt_registry_unittest.cc
@@ -0,0 +1,465 @@
+// 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"
+
+#include <windows.h>
+#include <rpc.h>
+#include <stddef.h>
+
+#include "base/test/test_reg_util_win.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+//------------------------------------------------------------------------------
+// WOW64 redirection tests
+//
+// - Only HKCU will be tested on the auto (try) bots.
+//   HKLM will be kept separate (and manual) for local testing only.
+//
+// NOTE: Currently no real WOW64 context testing, as building x86 projects
+//       during x64 builds is not currently supported for performance reasons.
+// https://cs.chromium.org/chromium/src/build/toolchain/win/BUILD.gn?sq%3Dpackage:chromium&l=314
+//------------------------------------------------------------------------------
+
+// Utility function for the WOW64 redirection test suites.
+// Note: Testing redirection through ADVAPI32 here as well, to get notice if
+//       expected behaviour changes!
+// If |redirected_path| == nullptr, no redirection is expected in any case.
+void DoRedirectTest(nt::ROOT_KEY nt_root_key,
+                    const wchar_t* path,
+                    const wchar_t* redirected_path OPTIONAL) {
+  HANDLE handle = INVALID_HANDLE_VALUE;
+  HKEY key_handle = nullptr;
+  constexpr ACCESS_MASK kAccess = KEY_WRITE | DELETE;
+  const HKEY root_key =
+      (nt_root_key == nt::HKCU) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
+
+  // Make sure clean before starting.
+  nt::DeleteRegKey(nt_root_key, nt::NONE, path);
+  if (redirected_path)
+    nt::DeleteRegKey(nt_root_key, nt::NONE, redirected_path);
+
+  //----------------------------------------------------------------------------
+  // No redirection through ADVAPI32 on straight x86 or x64.
+  ASSERT_EQ(ERROR_SUCCESS,
+            RegCreateKeyExW(root_key, path, 0, nullptr, REG_OPTION_NON_VOLATILE,
+                            kAccess, nullptr, &key_handle, nullptr));
+  ASSERT_EQ(ERROR_SUCCESS, RegCloseKey(key_handle));
+  ASSERT_TRUE(nt::OpenRegKey(nt_root_key, path, kAccess, &handle, nullptr));
+  ASSERT_TRUE(nt::DeleteRegKey(handle));
+  nt::CloseRegKey(handle);
+
+#ifdef _WIN64
+  //----------------------------------------------------------------------------
+  // Try forcing WOW64 redirection on x64 through ADVAPI32.
+  ASSERT_EQ(ERROR_SUCCESS,
+            RegCreateKeyExW(root_key, path, 0, nullptr, REG_OPTION_NON_VOLATILE,
+                            kAccess | KEY_WOW64_32KEY, nullptr, &key_handle,
+                            nullptr));
+  ASSERT_EQ(ERROR_SUCCESS, RegCloseKey(key_handle));
+  // Check path:
+  if (nt::OpenRegKey(nt_root_key, path, kAccess, &handle, nullptr)) {
+    if (redirected_path)
+      ADD_FAILURE();
+    ASSERT_TRUE(nt::DeleteRegKey(handle));
+    nt::CloseRegKey(handle);
+  } else if (!redirected_path) {
+    // Should have succeeded.
+    ADD_FAILURE();
+  }
+  if (redirected_path) {
+    // Check redirected path:
+    if (nt::OpenRegKey(nt_root_key, redirected_path, kAccess, &handle,
+                       nullptr)) {
+      if (!redirected_path)
+        ADD_FAILURE();
+      ASSERT_TRUE(nt::DeleteRegKey(handle));
+      nt::CloseRegKey(handle);
+    } else {
+      // Should have succeeded.
+      ADD_FAILURE();
+    }
+  }
+
+  //----------------------------------------------------------------------------
+  // Try forcing WOW64 redirection on x64 through NTDLL.
+  ASSERT_TRUE(
+      nt::CreateRegKey(nt_root_key, path, kAccess | KEY_WOW64_32KEY, nullptr));
+  // Check path:
+  if (nt::OpenRegKey(nt_root_key, path, kAccess, &handle, nullptr)) {
+    if (redirected_path)
+      ADD_FAILURE();
+    ASSERT_TRUE(nt::DeleteRegKey(handle));
+    nt::CloseRegKey(handle);
+  } else if (!redirected_path) {
+    // Should have succeeded.
+    ADD_FAILURE();
+  }
+  if (redirected_path) {
+    // Check redirected path:
+    if (nt::OpenRegKey(nt_root_key, redirected_path, kAccess, &handle,
+                       nullptr)) {
+      if (!redirected_path)
+        ADD_FAILURE();
+      ASSERT_TRUE(nt::DeleteRegKey(handle));
+      nt::CloseRegKey(handle);
+    } else {
+      // Should have succeeded.
+      ADD_FAILURE();
+    }
+  }
+#endif  // _WIN64
+}
+
+// These test reg paths match |kClassesSubtree| in nt_registry.cc.
+constexpr const wchar_t* kClassesRedirects[] = {
+    L"SOFTWARE\\Classes\\CLSID\\chrome_testing",
+    L"SOFTWARE\\Classes\\WOW6432Node\\CLSID\\chrome_testing",
+    L"SOFTWARE\\Classes\\DirectShow\\chrome_testing",
+    L"SOFTWARE\\Classes\\WOW6432Node\\DirectShow\\chrome_testing",
+    L"SOFTWARE\\Classes\\Interface\\chrome_testing",
+    L"SOFTWARE\\Classes\\WOW6432Node\\Interface\\chrome_testing",
+    L"SOFTWARE\\Classes\\Media Type\\chrome_testing",
+    L"SOFTWARE\\Classes\\WOW6432Node\\Media Type\\chrome_testing",
+    L"SOFTWARE\\Classes\\MediaFoundation\\chrome_testing",
+    L"SOFTWARE\\Classes\\WOW6432Node\\MediaFoundation\\chrome_testing"};
+
+static_assert((_countof(kClassesRedirects) & 0x01) == 0,
+              "Must have an even number of kClassesRedirects.");
+
+// This test does NOT use NtRegistryTest class.  It requires Windows WOW64
+// redirection to take place, which would not happen with a testing redirection
+// layer.
+TEST(NtRegistryTestRedirection, Wow64RedirectionHKCU) {
+  // Using two elements for each loop.
+  for (size_t index = 0; index < _countof(kClassesRedirects); index += 2) {
+    DoRedirectTest(nt::HKCU, kClassesRedirects[index],
+                   kClassesRedirects[index + 1]);
+  }
+}
+
+// These test reg paths match |kHklmSoftwareSubtree| in nt_registry.cc.
+constexpr const wchar_t* kHKLMNoRedirects[] = {
+    L"SOFTWARE\\Classes\\chrome_testing", L"SOFTWARE\\Clients\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\COM3\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Cryptography\\Calais\\Current\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Cryptography\\Calais\\Readers\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Cryptography\\Services\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\CTF\\SystemShared\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\CTF\\TIP\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\DFS\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Driver Signing\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\EnterpriseCertificates\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\EventSystem\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\MSMQ\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Non-Driver Signing\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Notepad\\DefaultFonts\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\OLE\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\RAS\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\RPC\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\SOFTWARE\\Microsoft\\Shared "
+    L"Tools\\MSInfo\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\SystemCertificates\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\TermServLicensing\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Transaction Server\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App "
+    L"Paths\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control "
+    L"Panel\\Cursors\\Schemes\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers"
+    L"\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons"
+    L"\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\KindMap"
+    L"\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Group "
+    L"Policy\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\PreviewHandlers"
+    L"\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations"
+    L"\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows "
+    L"NT\\CurrentVersion\\Console\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows "
+    L"NT\\CurrentVersion\\FontDpi\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows "
+    L"NT\\CurrentVersion\\FontLink\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows "
+    L"NT\\CurrentVersion\\FontMapper\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Fonts\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows "
+    L"NT\\CurrentVersion\\FontSubstitutes\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows "
+    L"NT\\CurrentVersion\\Gre_initialize\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution "
+    L"Options\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows "
+    L"NT\\CurrentVersion\\LanguagePack\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards"
+    L"\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows "
+    L"NT\\CurrentVersion\\Perflib\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Ports\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList"
+    L"\\chrome_testing",
+    L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time "
+    L"Zones\\chrome_testing",
+    L"SOFTWARE\\Policies\\chrome_testing",
+    L"SOFTWARE\\RegisteredApplications\\chrome_testing"};
+
+// Run from administrator command prompt!
+// Note: Disabled for automated testing (HKLM protection).  Local testing
+// only.
+//
+// This test does NOT use NtRegistryTest class.  It requires Windows WOW64
+// redirection to take place, which would not happen with a testing redirection
+// layer.
+TEST(NtRegistryTestRedirection, DISABLED_Wow64RedirectionHKLM) {
+  // 1) SOFTWARE is redirected.
+  DoRedirectTest(nt::HKLM, L"SOFTWARE\\chrome_testing",
+                 L"SOFTWARE\\WOW6432Node\\chrome_testing");
+
+  // 2) Except some subkeys are not.
+  for (size_t index = 0; index < _countof(kHKLMNoRedirects); ++index) {
+    DoRedirectTest(nt::HKLM, kHKLMNoRedirects[index], nullptr);
+  }
+
+  // 3) But then some Classes subkeys are redirected.
+  // Using two elements for each loop.
+  for (size_t index = 0; index < _countof(kClassesRedirects); index += 2) {
+    DoRedirectTest(nt::HKLM, kClassesRedirects[index],
+                   kClassesRedirects[index + 1]);
+  }
+
+  // 4) And just make sure other Classes subkeys are shared.
+  DoRedirectTest(nt::HKLM, L"SOFTWARE\\Classes\\chrome_testing", nullptr);
+}
+
+TEST(NtRegistryTestMisc, SanitizeSubkeyPaths) {
+  std::wstring new_path = L"";
+  EXPECT_TRUE(nt::SetTestingOverride(nt::HKCU, new_path));
+  std::wstring sani_path = nt::GetTestingOverride(nt::HKCU);
+  EXPECT_STREQ(L"", sani_path.c_str());
+
+  new_path = L"boo";
+  EXPECT_TRUE(nt::SetTestingOverride(nt::HKCU, new_path));
+  sani_path = nt::GetTestingOverride(nt::HKCU);
+  EXPECT_STREQ(L"boo", sani_path.c_str());
+
+  new_path = L"\\boo";
+  EXPECT_TRUE(nt::SetTestingOverride(nt::HKCU, new_path));
+  sani_path = nt::GetTestingOverride(nt::HKCU);
+  EXPECT_STREQ(L"boo", sani_path.c_str());
+
+  new_path = L"boo\\";
+  EXPECT_TRUE(nt::SetTestingOverride(nt::HKCU, new_path));
+  sani_path = nt::GetTestingOverride(nt::HKCU);
+  EXPECT_STREQ(L"boo", sani_path.c_str());
+
+  new_path = L"\\\\\\";
+  EXPECT_TRUE(nt::SetTestingOverride(nt::HKCU, new_path));
+  sani_path = nt::GetTestingOverride(nt::HKCU);
+  EXPECT_STREQ(L"", sani_path.c_str());
+
+  new_path = L"boo\\\\\\ya";
+  EXPECT_TRUE(nt::SetTestingOverride(nt::HKCU, new_path));
+  sani_path = nt::GetTestingOverride(nt::HKCU);
+  EXPECT_STREQ(L"boo\\ya", sani_path.c_str());
+
+  new_path = L"\\\\\\boo\\ya\\\\";
+  EXPECT_TRUE(nt::SetTestingOverride(nt::HKCU, new_path));
+  sani_path = nt::GetTestingOverride(nt::HKCU);
+  EXPECT_STREQ(L"boo\\ya", sani_path.c_str());
+
+  // Be sure to leave the environment clean.
+  new_path.clear();
+  EXPECT_TRUE(nt::SetTestingOverride(nt::HKCU, new_path));
+  sani_path = nt::GetTestingOverride(nt::HKCU);
+  EXPECT_STREQ(L"", sani_path.c_str());
+}
+
+//------------------------------------------------------------------------------
+// NtRegistryTest class
+//
+// Only use this class for tests that need testing registry redirection.
+//------------------------------------------------------------------------------
+
+class NtRegistryTest : public testing::Test {
+ protected:
+  void SetUp() override {
+    base::string16 temp;
+    override_manager_.OverrideRegistry(HKEY_CURRENT_USER, &temp);
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKCU, temp));
+    override_manager_.OverrideRegistry(HKEY_LOCAL_MACHINE, &temp);
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKLM, temp));
+  }
+
+  void TearDown() override {
+    base::string16 temp;
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKCU, temp));
+    ASSERT_TRUE(nt::SetTestingOverride(nt::HKLM, temp));
+  }
+
+ private:
+  registry_util::RegistryOverrideManager override_manager_;
+};
+
+//------------------------------------------------------------------------------
+// NT registry API tests
+//------------------------------------------------------------------------------
+
+TEST_F(NtRegistryTest, API_DWORD) {
+  HANDLE key_handle;
+  const wchar_t* dword_val_name = L"DwordTestValue";
+  DWORD dword_val = 1234;
+
+  // Create a subkey to play under.
+  ASSERT_TRUE(nt::CreateRegKey(nt::HKCU, L"NTRegistry\\dword", KEY_ALL_ACCESS,
+                               &key_handle));
+  ASSERT_NE(key_handle, INVALID_HANDLE_VALUE);
+  ASSERT_NE(key_handle, nullptr);
+
+  DWORD get_dword = 0;
+  EXPECT_FALSE(nt::QueryRegValueDWORD(key_handle, dword_val_name, &get_dword));
+
+  // Set
+  EXPECT_TRUE(nt::SetRegValueDWORD(key_handle, dword_val_name, dword_val));
+
+  // Get
+  EXPECT_TRUE(nt::QueryRegValueDWORD(key_handle, dword_val_name, &get_dword));
+  EXPECT_TRUE(get_dword == dword_val);
+
+  // Clean up
+  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
+  nt::CloseRegKey(key_handle);
+}
+
+TEST_F(NtRegistryTest, API_SZ) {
+  HANDLE key_handle;
+  const wchar_t* sz_val_name = L"SzTestValue";
+  std::wstring sz_val = L"blah de blah de blahhhhh.";
+  const wchar_t* sz_val_name2 = L"SzTestValueEmpty";
+  std::wstring sz_val2 = L"";
+
+  // Create a subkey to play under.
+  ASSERT_TRUE(nt::CreateRegKey(nt::HKCU, L"NTRegistry\\sz", KEY_ALL_ACCESS,
+                               &key_handle));
+  ASSERT_NE(key_handle, INVALID_HANDLE_VALUE);
+  ASSERT_NE(key_handle, nullptr);
+
+  std::wstring get_sz;
+  EXPECT_FALSE(nt::QueryRegValueSZ(key_handle, sz_val_name, &get_sz));
+
+  // Set
+  EXPECT_TRUE(nt::SetRegValueSZ(key_handle, sz_val_name, sz_val));
+  EXPECT_TRUE(nt::SetRegValueSZ(key_handle, sz_val_name2, sz_val2));
+
+  // Get
+  EXPECT_TRUE(nt::QueryRegValueSZ(key_handle, sz_val_name, &get_sz));
+  EXPECT_TRUE(get_sz.compare(sz_val) == 0);
+  EXPECT_TRUE(nt::QueryRegValueSZ(key_handle, sz_val_name2, &get_sz));
+  EXPECT_TRUE(get_sz.compare(sz_val2) == 0);
+
+  // Clean up
+  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
+  nt::CloseRegKey(key_handle);
+}
+
+TEST_F(NtRegistryTest, API_MULTISZ) {
+  HANDLE key_handle;
+  const wchar_t* multisz_val_name = L"SzmultiTestValue";
+  std::vector<std::wstring> multisz_val;
+  std::wstring multi1 = L"one";
+  std::wstring multi2 = L"two";
+  std::wstring multi3 = L"three";
+  const wchar_t* multisz_val_name2 = L"SzmultiTestValueBad";
+  std::wstring multi_empty = L"";
+
+  // Create a subkey to play under.
+  ASSERT_TRUE(nt::CreateRegKey(nt::HKCU, L"NTRegistry\\multisz", KEY_ALL_ACCESS,
+                               &key_handle));
+  ASSERT_NE(key_handle, INVALID_HANDLE_VALUE);
+  ASSERT_NE(key_handle, nullptr);
+
+  multisz_val.push_back(multi1);
+  multisz_val.push_back(multi2);
+  multisz_val.push_back(multi3);
+  // Set
+  EXPECT_TRUE(
+      nt::SetRegValueMULTISZ(key_handle, multisz_val_name, multisz_val));
+  multisz_val.clear();
+  multisz_val.push_back(multi_empty);
+  // Set
+  EXPECT_TRUE(
+      nt::SetRegValueMULTISZ(key_handle, multisz_val_name2, multisz_val));
+  multisz_val.clear();
+
+  // Get
+  EXPECT_TRUE(
+      nt::QueryRegValueMULTISZ(key_handle, multisz_val_name, &multisz_val));
+  if (multisz_val.size() == 3) {
+    EXPECT_TRUE(multi1.compare(multisz_val.at(0)) == 0);
+    EXPECT_TRUE(multi2.compare(multisz_val.at(1)) == 0);
+    EXPECT_TRUE(multi3.compare(multisz_val.at(2)) == 0);
+  } else {
+    EXPECT_TRUE(false);
+  }
+  multisz_val.clear();
+
+  // Get
+  EXPECT_TRUE(
+      nt::QueryRegValueMULTISZ(key_handle, multisz_val_name2, &multisz_val));
+  if (multisz_val.size() == 1) {
+    EXPECT_TRUE(multi_empty.compare(multisz_val.at(0)) == 0);
+  } else {
+    EXPECT_TRUE(false);
+  }
+  multisz_val.clear();
+
+  // Clean up
+  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
+  nt::CloseRegKey(key_handle);
+}
+
+TEST_F(NtRegistryTest, CreateRegKeyRecursion) {
+  HANDLE key_handle;
+  const wchar_t* sz_new_key_1 = L"test1\\new\\subkey";
+  const wchar_t* sz_new_key_2 = L"test2\\new\\subkey\\blah\\";
+  const wchar_t* sz_new_key_3 = L"\\test3\\new\\subkey\\\\blah2";
+
+  // Tests for CreateRegKey recursion.
+  ASSERT_TRUE(
+      nt::CreateRegKey(nt::HKCU, sz_new_key_1, KEY_ALL_ACCESS, nullptr));
+  EXPECT_TRUE(nt::OpenRegKey(nt::HKCU, sz_new_key_1, KEY_ALL_ACCESS,
+                             &key_handle, nullptr));
+  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
+  nt::CloseRegKey(key_handle);
+
+  ASSERT_TRUE(
+      nt::CreateRegKey(nt::HKCU, sz_new_key_2, KEY_ALL_ACCESS, nullptr));
+  EXPECT_TRUE(nt::OpenRegKey(nt::HKCU, sz_new_key_2, KEY_ALL_ACCESS,
+                             &key_handle, nullptr));
+  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
+  nt::CloseRegKey(key_handle);
+
+  ASSERT_TRUE(
+      nt::CreateRegKey(nt::HKCU, sz_new_key_3, KEY_ALL_ACCESS, nullptr));
+  EXPECT_TRUE(nt::OpenRegKey(nt::HKCU, L"test3\\new\\subkey\\blah2",
+                             KEY_ALL_ACCESS, &key_handle, nullptr));
+  EXPECT_TRUE(nt::DeleteRegKey(key_handle));
+  nt::CloseRegKey(key_handle);
+
+  // Subkey path can be null.
+  ASSERT_TRUE(nt::CreateRegKey(nt::HKCU, nullptr, KEY_ALL_ACCESS, &key_handle));
+  ASSERT_NE(key_handle, INVALID_HANDLE_VALUE);
+  ASSERT_NE(key_handle, nullptr);
+  nt::CloseRegKey(key_handle);
+}
+
+}  // namespace