Commit 5379f172 authored by pennymac's avatar pennymac Committed by Commit bot

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}
parent 546e56fe
...@@ -11,7 +11,7 @@ assert(is_win) ...@@ -11,7 +11,7 @@ assert(is_win)
# Please don't add dependencies on other system libraries. # Please don't add dependencies on other system libraries.
static_library("install_static_util") { static_library("install_static_util") {
public_deps = [ public_deps = [
"//chrome_elf/nt_registry:nt_registry", "//chrome_elf:nt_registry",
] ]
sources = [ sources = [
......
...@@ -270,7 +270,8 @@ bool GetCollectStatsConsentImpl(const std::wstring& exe_path) { ...@@ -270,7 +270,8 @@ bool GetCollectStatsConsentImpl(const std::wstring& exe_path) {
std::wstring full_key_path(kRegPathClientStateMedium); std::wstring full_key_path(kRegPathClientStateMedium);
full_key_path.append(1, L'\\'); full_key_path.append(1, L'\\');
full_key_path.append(app_guid); full_key_path.append(app_guid);
if (system_install && nt::QueryRegValueDWORD(nt::HKLM, full_key_path.c_str(), if (system_install &&
nt::QueryRegValueDWORD(nt::HKLM, nt::WOW6432, full_key_path.c_str(),
kRegValueUsageStats, &out_value)) kRegValueUsageStats, &out_value))
return (out_value == 1); return (out_value == 1);
...@@ -279,8 +280,8 @@ bool GetCollectStatsConsentImpl(const std::wstring& exe_path) { ...@@ -279,8 +280,8 @@ bool GetCollectStatsConsentImpl(const std::wstring& exe_path) {
full_key_path.append(1, L'\\'); full_key_path.append(1, L'\\');
full_key_path.append(app_guid); full_key_path.append(app_guid);
return (nt::QueryRegValueDWORD((system_install ? nt::HKLM : nt::HKCU), return (nt::QueryRegValueDWORD((system_install ? nt::HKLM : nt::HKCU),
full_key_path.c_str(), kRegValueUsageStats, nt::WOW6432, full_key_path.c_str(),
&out_value) && kRegValueUsageStats, &out_value) &&
out_value == 1); out_value == 1);
} }
...@@ -410,8 +411,8 @@ bool IsMultiInstall(bool is_system_install) { ...@@ -410,8 +411,8 @@ bool IsMultiInstall(bool is_system_install) {
full_key_path.append(1, L'\\'); full_key_path.append(1, L'\\');
full_key_path.append(kAppGuidGoogleChrome); full_key_path.append(kAppGuidGoogleChrome);
if (!nt::QueryRegValueSZ((is_system_install ? nt::HKLM : nt::HKCU), if (!nt::QueryRegValueSZ((is_system_install ? nt::HKLM : nt::HKCU),
full_key_path.c_str(), kUninstallArgumentsField, nt::WOW6432, full_key_path.c_str(),
&args)) kUninstallArgumentsField, &args))
return false; return false;
return (args.find(L"--multi-install") != std::wstring::npos); return (args.find(L"--multi-install") != std::wstring::npos);
...@@ -429,7 +430,7 @@ bool GetCollectStatsInSample() { ...@@ -429,7 +430,7 @@ bool GetCollectStatsInSample() {
std::wstring registry_path = GetChromeInstallRegistryPath(); std::wstring registry_path = GetChromeInstallRegistryPath();
DWORD out_value = 0; 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)) { kRegValueChromeStatsSample, &out_value)) {
// If reading the value failed, treat it as though sampling isn't in effect, // If reading the value failed, treat it as though sampling isn't in effect,
// implicitly meaning this install is in the sample. // implicitly meaning this install is in the sample.
...@@ -442,8 +443,8 @@ bool SetCollectStatsInSample(bool in_sample) { ...@@ -442,8 +443,8 @@ bool SetCollectStatsInSample(bool in_sample) {
std::wstring registry_path = GetChromeInstallRegistryPath(); std::wstring registry_path = GetChromeInstallRegistryPath();
HANDLE key_handle = INVALID_HANDLE_VALUE; HANDLE key_handle = INVALID_HANDLE_VALUE;
if (!nt::CreateRegKey(nt::HKCU, registry_path.c_str(), KEY_SET_VALUE, if (!nt::CreateRegKey(nt::HKCU, registry_path.c_str(),
&key_handle)) { KEY_SET_VALUE | KEY_WOW64_32KEY, &key_handle)) {
nt::CloseRegKey(key_handle); nt::CloseRegKey(key_handle);
return false; return false;
} }
...@@ -456,14 +457,14 @@ bool ReportingIsEnforcedByPolicy(bool* crash_reporting_enabled) { ...@@ -456,14 +457,14 @@ bool ReportingIsEnforcedByPolicy(bool* crash_reporting_enabled) {
DWORD value = 0; DWORD value = 0;
// First, try HKLM. // First, try HKLM.
if (nt::QueryRegValueDWORD(nt::HKLM, kRegPathChromePolicy, if (nt::QueryRegValueDWORD(nt::HKLM, nt::NONE, kRegPathChromePolicy,
kMetricsReportingEnabled, &value)) { kMetricsReportingEnabled, &value)) {
*crash_reporting_enabled = (value != 0); *crash_reporting_enabled = (value != 0);
return true; return true;
} }
// Second, try HKCU. // Second, try HKCU.
if (nt::QueryRegValueDWORD(nt::HKCU, kRegPathChromePolicy, if (nt::QueryRegValueDWORD(nt::HKCU, nt::NONE, kRegPathChromePolicy,
kMetricsReportingEnabled, &value)) { kMetricsReportingEnabled, &value)) {
*crash_reporting_enabled = (value != 0); *crash_reporting_enabled = (value != 0);
return true; return true;
...@@ -643,13 +644,13 @@ void GetChromeChannelName(bool is_per_user_install, ...@@ -643,13 +644,13 @@ void GetChromeChannelName(bool is_per_user_install,
std::wstring full_key_path(kRegPathClientState); std::wstring full_key_path(kRegPathClientState);
full_key_path.append(1, L'\\'); full_key_path.append(1, L'\\');
full_key_path.append(kAppGuidGoogleBinaries); full_key_path.append(kAppGuidGoogleBinaries);
nt::QueryRegValueSZ(is_per_user_install ? nt::HKCU : nt::HKLM, nt::QueryRegValueSZ(is_per_user_install ? nt::HKCU : nt::HKLM, nt::WOW6432,
full_key_path.c_str(), kRegApField, &value); full_key_path.c_str(), kRegApField, &value);
} else { } else {
std::wstring full_key_path(kRegPathClientState); std::wstring full_key_path(kRegPathClientState);
full_key_path.append(1, L'\\'); full_key_path.append(1, L'\\');
full_key_path.append(kAppGuidGoogleChrome); full_key_path.append(kAppGuidGoogleChrome);
nt::QueryRegValueSZ(is_per_user_install ? nt::HKCU : nt::HKLM, nt::QueryRegValueSZ(is_per_user_install ? nt::HKCU : nt::HKLM, nt::WOW6432,
full_key_path.c_str(), kRegApField, &value); full_key_path.c_str(), kRegApField, &value);
} }
...@@ -695,7 +696,7 @@ std::string GetGoogleUpdateVersion() { ...@@ -695,7 +696,7 @@ std::string GetGoogleUpdateVersion() {
// Consider whether Chromium should connect to Google update to manage // Consider whether Chromium should connect to Google update to manage
// updates. Should this be returning an empty string for Chromium builds?. // updates. Should this be returning an empty string for Chromium builds?.
std::wstring update_version; std::wstring update_version;
if (nt::QueryRegValueSZ(nt::AUTO, kRegPathGoogleUpdate, if (nt::QueryRegValueSZ(nt::AUTO, nt::WOW6432, kRegPathGoogleUpdate,
kRegGoogleUpdateVersion, &update_version)) kRegGoogleUpdateVersion, &update_version))
return UTF16ToUTF8(update_version); return UTF16ToUTF8(update_version);
......
...@@ -43,10 +43,10 @@ shared_library("chrome_elf") { ...@@ -43,10 +43,10 @@ shared_library("chrome_elf") {
":constants", ":constants",
":crash", ":crash",
":hook_util", ":hook_util",
":nt_registry",
":security", ":security",
"//build/config/sanitizers:deps", "//build/config/sanitizers:deps",
"//chrome/install_static:install_static_util", "//chrome/install_static:install_static_util",
"//chrome_elf/nt_registry:nt_registry",
] ]
configs += [ "//build/config/win:windowed" ] configs += [ "//build/config/win:windowed" ]
configs -= [ "//build/config/win:console" ] configs -= [ "//build/config/win:console" ]
...@@ -76,7 +76,7 @@ source_set("security") { ...@@ -76,7 +76,7 @@ source_set("security") {
] ]
deps = [ deps = [
":constants", ":constants",
"//chrome_elf/nt_registry:nt_registry", ":nt_registry",
] ]
} }
...@@ -126,9 +126,9 @@ static_library("blacklist") { ...@@ -126,9 +126,9 @@ static_library("blacklist") {
":constants", ":constants",
":crash", ":crash",
":hook_util", ":hook_util",
":nt_registry",
"//base:base_static", # pe_image "//base:base_static", # pe_image
"//chrome/install_static:install_static_util", "//chrome/install_static:install_static_util",
"//chrome_elf/nt_registry:nt_registry",
] ]
} }
...@@ -159,11 +159,24 @@ static_library("hook_util") { ...@@ -159,11 +159,24 @@ static_library("hook_util") {
"hook_util/hook_util.h", "hook_util/hook_util.h",
] ]
deps = [ deps = [
":nt_registry", # utils
"//base:base_static", # pe_image "//base:base_static", # pe_image
"//sandbox", "//sandbox",
] ]
} }
# 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 ## tests
##------------------------------------------------------------------------------ ##------------------------------------------------------------------------------
...@@ -175,6 +188,7 @@ test("chrome_elf_unittests") { ...@@ -175,6 +188,7 @@ test("chrome_elf_unittests") {
"chrome_elf_util_unittest.cc", "chrome_elf_util_unittest.cc",
"elf_imports_unittest.cc", "elf_imports_unittest.cc",
"hook_util/test/hook_util_test.cc", "hook_util/test/hook_util_test.cc",
"nt_registry/nt_registry_unittest.cc",
"run_all_unittests.cc", "run_all_unittests.cc",
] ]
include_dirs = [ "$target_gen_dir" ] include_dirs = [ "$target_gen_dir" ]
...@@ -185,13 +199,13 @@ test("chrome_elf_unittests") { ...@@ -185,13 +199,13 @@ test("chrome_elf_unittests") {
":crash", ":crash",
":hook_util", ":hook_util",
":hook_util_test_dll", ":hook_util_test_dll",
":nt_registry",
":security", ":security",
"//base", "//base",
"//base/test:test_support", "//base/test:test_support",
"//chrome", "//chrome",
"//chrome/common:version_header", "//chrome/common:version_header",
"//chrome/install_static:install_static_util", "//chrome/install_static:install_static_util",
"//chrome_elf/nt_registry:nt_registry",
"//sandbox", "//sandbox",
"//testing/gtest", "//testing/gtest",
] ]
...@@ -222,20 +236,22 @@ test("chrome_elf_unittests") { ...@@ -222,20 +236,22 @@ test("chrome_elf_unittests") {
} }
shared_library("blacklist_test_main_dll") { shared_library("blacklist_test_main_dll") {
testonly = true
sources = [ sources = [
"blacklist/test/blacklist_test_main_dll.cc", "blacklist/test/blacklist_test_main_dll.cc",
"blacklist/test/blacklist_test_main_dll.def", "blacklist/test/blacklist_test_main_dll.def",
] ]
deps = [ deps = [
":blacklist", ":blacklist",
":nt_registry",
"//base", "//base",
"//build/config/sanitizers:deps", "//build/config/sanitizers:deps",
"//chrome/install_static:install_static_util", "//chrome/install_static:install_static_util",
"//chrome_elf/nt_registry:nt_registry",
] ]
} }
loadable_module("blacklist_test_dll_1") { loadable_module("blacklist_test_dll_1") {
testonly = true
sources = [ sources = [
"blacklist/test/blacklist_test_dll_1.cc", "blacklist/test/blacklist_test_dll_1.cc",
"blacklist/test/blacklist_test_dll_1.def", "blacklist/test/blacklist_test_dll_1.def",
...@@ -246,6 +262,7 @@ loadable_module("blacklist_test_dll_1") { ...@@ -246,6 +262,7 @@ loadable_module("blacklist_test_dll_1") {
} }
loadable_module("blacklist_test_dll_2") { loadable_module("blacklist_test_dll_2") {
testonly = true
sources = [ sources = [
"blacklist/test/blacklist_test_dll_2.cc", "blacklist/test/blacklist_test_dll_2.cc",
"blacklist/test/blacklist_test_dll_2.def", "blacklist/test/blacklist_test_dll_2.def",
...@@ -261,6 +278,7 @@ loadable_module("blacklist_test_dll_2") { ...@@ -261,6 +278,7 @@ loadable_module("blacklist_test_dll_2") {
# which does), Ninja would get confused and always rebuild this target because # which does), Ninja would get confused and always rebuild this target because
# it sees a declared output file but that file doesn't exist on disk. # it sees a declared output file but that file doesn't exist on disk.
loadable_module("blacklist_test_dll_3") { loadable_module("blacklist_test_dll_3") {
testonly = true
sources = [ sources = [
"blacklist/test/blacklist_test_dll_3.cc", "blacklist/test/blacklist_test_dll_3.cc",
] ]
...@@ -270,6 +288,7 @@ loadable_module("blacklist_test_dll_3") { ...@@ -270,6 +288,7 @@ loadable_module("blacklist_test_dll_3") {
} }
shared_library("hook_util_test_dll") { shared_library("hook_util_test_dll") {
testonly = true
sources = [ sources = [
"hook_util/test/hook_util_test_dll.cc", "hook_util/test/hook_util_test_dll.cc",
"hook_util/test/hook_util_test_dll.h", "hook_util/test/hook_util_test_dll.h",
......
...@@ -133,20 +133,18 @@ class BlacklistTest : public testing::Test { ...@@ -133,20 +133,18 @@ class BlacklistTest : public testing::Test {
// process-specific environment variables, for our test DLLs to access. // process-specific environment variables, for our test DLLs to access.
// This will only work as long as the IPC is within the same process. // This will only work as long as the IPC is within the same process.
void IpcOverrides() { void IpcOverrides() {
if (::wcslen(nt::HKCU_override) != 0) { base::string16 temp = nt::GetTestingOverride(nt::HKCU);
ASSERT_TRUE( if (!temp.empty())
::SetEnvironmentVariableW(L"hkcu_override", nt::HKCU_override)); ASSERT_TRUE(::SetEnvironmentVariableW(L"hkcu_override", temp.c_str()));
} temp = nt::GetTestingOverride(nt::HKLM);
if (::wcslen(nt::HKLM_override) != 0) { if (!temp.empty())
ASSERT_TRUE( ASSERT_TRUE(::SetEnvironmentVariableW(L"hklm_override", temp.c_str()));
::SetEnvironmentVariableW(L"hklm_override", nt::HKLM_override));
}
} }
void SetUp() override { void SetUp() override {
base::string16 temp; base::string16 temp;
override_manager_.OverrideRegistry(HKEY_CURRENT_USER, &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. // Make the override path available to our test DLL.
IpcOverrides(); IpcOverrides();
...@@ -203,10 +201,9 @@ class BlacklistTest : public testing::Test { ...@@ -203,10 +201,9 @@ class BlacklistTest : public testing::Test {
TestDll_RemoveDllFromBlacklist(kTestDllName1); TestDll_RemoveDllFromBlacklist(kTestDllName1);
TestDll_RemoveDllFromBlacklist(kTestDllName2); TestDll_RemoveDllFromBlacklist(kTestDllName2);
TestDll_RemoveDllFromBlacklist(kTestDllName3); TestDll_RemoveDllFromBlacklist(kTestDllName3);
}
// A scoped temporary directory to be destroyed with this test. ASSERT_TRUE(nt::SetTestingOverride(nt::HKCU, base::string16()));
base::ScopedTempDir reg_override_dir_; }
}; };
TEST_F(BlacklistTest, Beacon) { TEST_F(BlacklistTest, Beacon) {
......
...@@ -14,22 +14,20 @@ namespace { ...@@ -14,22 +14,20 @@ namespace {
void GetIpcOverrides() { void GetIpcOverrides() {
DWORD buffer_size = ::GetEnvironmentVariableW(L"hkcu_override", nullptr, 0); DWORD buffer_size = ::GetEnvironmentVariableW(L"hkcu_override", nullptr, 0);
if (buffer_size > 0) { if (buffer_size > 0) {
wchar_t* content = new wchar_t[buffer_size]; std::wstring content(buffer_size, L'\0');
buffer_size = buffer_size =
::GetEnvironmentVariableW(L"hkcu_override", content, buffer_size); ::GetEnvironmentVariableW(L"hkcu_override", &content[0], buffer_size);
if (buffer_size) if (buffer_size)
::wcsncpy(nt::HKCU_override, content, nt::g_kRegMaxPathLen - 1); nt::SetTestingOverride(nt::HKCU, content);
delete[] content;
} }
buffer_size = ::GetEnvironmentVariableW(L"hklm_override", nullptr, 0); buffer_size = ::GetEnvironmentVariableW(L"hklm_override", nullptr, 0);
if (buffer_size > 0) { if (buffer_size > 0) {
wchar_t* content = new wchar_t[buffer_size]; std::wstring content(buffer_size, L'\0');
buffer_size = buffer_size =
::GetEnvironmentVariableW(L"hklm_override", content, buffer_size); ::GetEnvironmentVariableW(L"hklm_override", &content[0], buffer_size);
if (buffer_size) if (buffer_size)
::wcsncpy(nt::HKLM_override, content, nt::g_kRegMaxPathLen - 1); nt::SetTestingOverride(nt::HKLM, content);
delete[] content;
} }
return; return;
......
...@@ -72,17 +72,25 @@ bool IsSecuritySet() { ...@@ -72,17 +72,25 @@ bool IsSecuritySet() {
} }
void RegRedirect(nt::ROOT_KEY key, void RegRedirect(nt::ROOT_KEY key,
registry_util::RegistryOverrideManager& rom) { registry_util::RegistryOverrideManager* rom) {
ASSERT_NE(key, nt::AUTO);
base::string16 temp; base::string16 temp;
if (key == nt::HKCU) { if (key == nt::HKCU) {
rom.OverrideRegistry(HKEY_CURRENT_USER, &temp); rom->OverrideRegistry(HKEY_CURRENT_USER, &temp);
::wcsncpy(nt::HKCU_override, temp.c_str(), nt::g_kRegMaxPathLen - 1); ASSERT_TRUE(nt::SetTestingOverride(nt::HKCU, temp));
} else if (key == nt::HKLM) { } else {
rom.OverrideRegistry(HKEY_LOCAL_MACHINE, &temp); rom->OverrideRegistry(HKEY_LOCAL_MACHINE, &temp);
::wcsncpy(nt::HKLM_override, temp.c_str(), nt::g_kRegMaxPathLen - 1); 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) { TEST(ChromeElfUtilTest, CanaryTest) {
...@@ -108,7 +116,7 @@ TEST(ChromeElfUtilTest, BrowserProcessSecurityTest) { ...@@ -108,7 +116,7 @@ TEST(ChromeElfUtilTest, BrowserProcessSecurityTest) {
// Set up registry override for this test. // Set up registry override for this test.
registry_util::RegistryOverrideManager override_manager; registry_util::RegistryOverrideManager override_manager;
RegRedirect(nt::HKCU, override_manager); RegRedirect(nt::HKCU, &override_manager);
// First, ensure that the emergency-off finch signal works. // First, ensure that the emergency-off finch signal works.
EXPECT_TRUE(SetSecurityFinchFlag(true)); EXPECT_TRUE(SetSecurityFinchFlag(true));
...@@ -119,112 +127,8 @@ TEST(ChromeElfUtilTest, BrowserProcessSecurityTest) { ...@@ -119,112 +127,8 @@ TEST(ChromeElfUtilTest, BrowserProcessSecurityTest) {
// Second, test that the process mitigation is set when no finch signal. // Second, test that the process mitigation is set when no finch signal.
elf_security::EarlyBrowserSecurity(); elf_security::EarlyBrowserSecurity();
EXPECT_TRUE(IsSecuritySet()); EXPECT_TRUE(IsSecuritySet());
}
//------------------------------------------------------------------------------ CancelRegRedirect(nt::HKCU);
// 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);
} }
// Parameterized test with paramters: // Parameterized test with paramters:
...@@ -237,8 +141,8 @@ class ChromeElfUtilTest ...@@ -237,8 +141,8 @@ class ChromeElfUtilTest
protected: protected:
void SetUp() override { void SetUp() override {
// Set up registry override for these tests. // Set up registry override for these tests.
RegRedirect(nt::HKLM, override_manager_); RegRedirect(nt::HKLM, &override_manager_);
RegRedirect(nt::HKCU, override_manager_); RegRedirect(nt::HKCU, &override_manager_);
const char* app; const char* app;
const char* level; const char* level;
...@@ -263,6 +167,11 @@ class ChromeElfUtilTest ...@@ -263,6 +167,11 @@ class ChromeElfUtilTest
} }
} }
void TearDown() override {
CancelRegRedirect(nt::HKCU);
CancelRegRedirect(nt::HKLM);
}
base::string16 BuildKey(const wchar_t* path, const wchar_t* guid) { base::string16 BuildKey(const wchar_t* path, const wchar_t* guid) {
base::string16 full_key_path(path); base::string16 full_key_path(path);
full_key_path.append(1, L'\\'); full_key_path.append(1, L'\\');
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <versionhelpers.h> // windows.h must be before #include <versionhelpers.h> // windows.h must be before
#include "base/win/pe_image.h" #include "base/win/pe_image.h"
#include "chrome_elf/nt_registry/nt_registry.h" // utils
#include "sandbox/win/src/interception_internal.h" #include "sandbox/win/src/interception_internal.h"
#include "sandbox/win/src/internal_types.h" #include "sandbox/win/src/internal_types.h"
#include "sandbox/win/src/sandbox_utils.h" #include "sandbox/win/src/sandbox_utils.h"
...@@ -18,31 +19,6 @@ namespace { ...@@ -18,31 +19,6 @@ namespace {
// Common hooking utility functions - LOCAL // Common hooking utility functions - LOCAL
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
#if !defined(_WIN64)
// Whether a process is running under WOW64 (the wrapper that allows 32-bit
// processes to run on 64-bit versions of Windows). This will return
// WOW64_DISABLED for both "32-bit Chrome on 32-bit Windows" and "64-bit
// Chrome on 64-bit Windows". WOW64_UNKNOWN means "an error occurred", e.g.
// the process does not have sufficient access rights to determine this.
enum WOW64Status {
WOW64_DISABLED,
WOW64_ENABLED,
WOW64_UNKNOWN,
};
WOW64Status GetWOW64StatusForCurrentProcess() {
typedef BOOL(WINAPI * IsWow64ProcessFunc)(HANDLE, PBOOL);
IsWow64ProcessFunc is_wow64_process = reinterpret_cast<IsWow64ProcessFunc>(
GetProcAddress(GetModuleHandle(L"kernel32.dll"), "IsWow64Process"));
if (!is_wow64_process)
return WOW64_DISABLED;
BOOL is_wow64 = FALSE;
if (!is_wow64_process(GetCurrentProcess(), &is_wow64))
return WOW64_UNKNOWN;
return is_wow64 ? WOW64_ENABLED : WOW64_DISABLED;
}
#endif // !defined(_WIN64)
// Change the page protections to writable, copy the data, // Change the page protections to writable, copy the data,
// restore protections. Returns a winerror code. // restore protections. Returns a winerror code.
DWORD PatchMem(void* target, void* new_bytes, size_t length) { DWORD PatchMem(void* target, void* new_bytes, size_t length) {
...@@ -246,7 +222,7 @@ sandbox::ServiceResolverThunk* HookSystemService(bool relaxed) { ...@@ -246,7 +222,7 @@ sandbox::ServiceResolverThunk* HookSystemService(bool relaxed) {
// handling one like it does in 32-bit versions). // handling one like it does in 32-bit versions).
thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); thunk = new sandbox::ServiceResolverThunk(current_process, relaxed);
#else #else
if (GetWOW64StatusForCurrentProcess() == WOW64_ENABLED) { if (nt::IsCurrentProcWow64()) {
if (::IsWindows10OrGreater()) if (::IsWindows10OrGreater())
thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed); thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed);
else if (::IsWindows8OrGreater()) else if (::IsWindows8OrGreater())
......
# 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" ]
}
...@@ -6,3 +6,8 @@ include_rules = [ ...@@ -6,3 +6,8 @@ include_rules = [
"-chrome_elf", "-chrome_elf",
"+chrome_elf/nt_registry/nt_registry.h", "+chrome_elf/nt_registry/nt_registry.h",
] ]
specific_include_rules = {
".*_unittest\.cc": [
"+base",
]
}
\ No newline at end of file
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
#include "chrome_elf/nt_registry/nt_registry.h" #include "chrome_elf/nt_registry/nt_registry.h"
#include <assert.h>
#include <stdlib.h>
namespace { namespace {
// Function pointers used for registry access. // Function pointers used for registry access.
...@@ -18,14 +21,20 @@ NtSetValueKeyFunction g_nt_set_value_key = nullptr; ...@@ -18,14 +21,20 @@ NtSetValueKeyFunction g_nt_set_value_key = nullptr;
// Lazy init. No concern about concurrency in chrome_elf. // Lazy init. No concern about concurrency in chrome_elf.
bool g_initialized = false; bool g_initialized = false;
bool g_system_install = false; bool g_system_install = false;
bool g_reg_redirection = false; bool g_wow64_proc = false;
const size_t g_kMaxPathLen = 255;
wchar_t g_kRegPathHKLM[] = L"\\Registry\\Machine\\"; wchar_t g_kRegPathHKLM[] = L"\\Registry\\Machine\\";
wchar_t g_kRegPathHKCU[g_kMaxPathLen] = L""; wchar_t g_kRegPathHKCU[nt::g_kRegMaxPathLen + 1] = L"";
wchar_t g_current_user_sid_string[g_kMaxPathLen] = L""; wchar_t g_current_user_sid_string[nt::g_kRegMaxPathLen + 1] = L"";
wchar_t g_override_path[g_kMaxPathLen] = 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() { bool IsThisProcSystem() {
wchar_t program_dir[MAX_PATH] = {}; wchar_t program_dir[MAX_PATH] = {};
wchar_t* cmd_line = GetCommandLineW(); wchar_t* cmd_line = GetCommandLineW();
...@@ -42,6 +51,22 @@ bool IsThisProcSystem() { ...@@ -42,6 +51,22 @@ bool IsThisProcSystem() {
return false; 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() { bool InitNativeRegApi() {
HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll"); HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
...@@ -89,96 +114,415 @@ bool InitNativeRegApi() { ...@@ -89,96 +114,415 @@ bool InitNativeRegApi() {
return false; return false;
// Finish setting up global HKCU path. // 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"\\", ::wcsncat(g_kRegPathHKCU, L"\\",
(g_kMaxPathLen - ::wcslen(g_kRegPathHKCU) - 1)); (nt::g_kRegMaxPathLen - ::wcslen(g_kRegPathHKCU)));
// Keep the sid string as well. // Keep the sid string as well.
wchar_t* ptr = ::wcsrchr(current_user_reg_path.Buffer, L'\\'); wchar_t* ptr = ::wcsrchr(current_user_reg_path.Buffer, L'\\');
ptr++; 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); 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(); g_system_install = IsThisProcSystem();
// Figure out if this is a WOW64 process.
g_wow64_proc = IsThisProcWow64();
g_initialized = true; g_initialized = true;
return 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) { // This enum defines states for how to handle redirection.
// AUTO // NOTE: When WOW64 redirection should happen, the redirect subkey can be either
key = g_system_install ? nt::HKLM : nt::HKCU; // 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 (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 ((key == nt::HKCU) && (::wcslen(nt::HKCU_override) != 0)) { if (root == nt::HKCU && *g_HKCU_override) {
std::wstring temp(g_kRegPathHKCU); std::wstring temp = g_kRegPathHKCU;
temp.append(nt::HKCU_override); temp.append(g_HKCU_override);
temp.append(L"\\"); temp.append(L"\\");
::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1); return temp;
g_reg_redirection = true; } else if (root == nt::HKLM && *g_HKLM_override) {
return g_override_path; // Yes, HKLM override goes into HKCU. This is not a typo.
} else if ((key == nt::HKLM) && (::wcslen(nt::HKLM_override) != 0)) { std::wstring temp = g_kRegPathHKCU;
std::wstring temp(g_kRegPathHKCU); temp.append(g_HKLM_override);
temp.append(nt::HKLM_override);
temp.append(L"\\"); temp.append(L"\\");
::wcsncpy(g_override_path, temp.c_str(), g_kMaxPathLen - 1); return temp;
g_reg_redirection = true;
return g_override_path;
} }
g_reg_redirection = false; return (root == nt::HKCU) ? g_kRegPathHKCU : g_kRegPathHKLM;
if (key == nt::HKCU) }
return g_kRegPathHKCU;
// 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 else
return g_kRegPathHKLM; index = input->find_first_of(L"\\", index + 1);
}
} }
// Turns a root and subkey path into the registry base hive and the rest of the // Turns a root and subkey path into the registry base hive and the rest of the
// subkey tokens. // subkey tokens.
// - |converted_root| should come directly out of ConvertRootKey function. // - |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>\". // - E.g. base hive: "\Registry\Machine\", "\Registry\User\<SID>\".
bool ParseFullRegPath(const wchar_t* converted_root, bool ParseFullRegPath(const std::wstring& converted_root,
const wchar_t* subkey_path, const std::wstring& subkey_path,
std::wstring* out_base, std::wstring* out_base,
std::vector<std::wstring>* subkeys) { std::vector<std::wstring>* subkeys) {
out_base->clear(); out_base->clear();
subkeys->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. // Why process |converted_root|? To handle reg redirection used by tests.
// E.g.: // E.g.:
// |converted_root| = "\REGISTRY\USER\S-1-5-21-39260824-743453154-142223018- // |converted_root| = "\REGISTRY\USER\S-1-5-21-39260824-743453154-142223018-
// 716772\Software\Chromium\TempTestKeys\13110669370890870$94c6ed9d-bc34- // 716772\Software\Chromium\TempTestKeys\13110669370890870$94c6ed9d-bc34-
// 44f3-a0b3-9eee2d3f2f82\". // 44f3-a0b3-9eee2d3f2f82\".
// |subkey_path| = "SOFTWARE\Google\Chrome\BrowserSec". // |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_path.append(subkey_path);
temp.append(subkey_path);
// Tokenize the full path. // Tokenize the full path.
size_t find_start = 0; 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) { while (delimiter != std::wstring::npos) {
std::wstring token = temp.substr(find_start, delimiter - find_start); subkeys->emplace_back(temp_path, find_start, delimiter - find_start);
if (!token.empty()) // Move past the backslash.
subkeys->push_back(token);
find_start = delimiter + 1; 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 if there is one.
// Get the last token. if (!temp_path.empty())
subkeys->push_back(temp.substr(find_start)); 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. // The base hive for HKCU needs to include the user SID.
uint32_t num_base_tokens = 2; uint32_t num_base_tokens = 2;
const wchar_t* hkcu = L"\\REGISTRY\\USER\\"; if (0 == temp_path.compare(0, 14, L"REGISTRY\\USER\\"))
if (0 == ::wcsnicmp(converted_root, hkcu, ::wcslen(hkcu)))
num_base_tokens = 3; num_base_tokens = 3;
if (subkeys->size() < num_base_tokens) if (subkeys->size() < num_base_tokens)
...@@ -186,8 +530,8 @@ bool ParseFullRegPath(const wchar_t* converted_root, ...@@ -186,8 +530,8 @@ bool ParseFullRegPath(const wchar_t* converted_root,
// Pull out the base hive tokens. // Pull out the base hive tokens.
out_base->push_back(L'\\'); out_base->push_back(L'\\');
for (size_t i = 0; i < num_base_tokens; i++) { for (size_t i = 0; i < num_base_tokens; ++i) {
out_base->append((*subkeys)[i].c_str()); out_base->append((*subkeys)[i]);
out_base->push_back(L'\\'); out_base->push_back(L'\\');
} }
subkeys->erase(subkeys->begin(), subkeys->begin() + num_base_tokens); subkeys->erase(subkeys->begin(), subkeys->begin() + num_base_tokens);
...@@ -198,6 +542,10 @@ bool ParseFullRegPath(const wchar_t* converted_root, ...@@ -198,6 +542,10 @@ bool ParseFullRegPath(const wchar_t* converted_root,
return true; return true;
} }
//------------------------------------------------------------------------------
// Misc wrapper functions - LOCAL
//------------------------------------------------------------------------------
NTSTATUS CreateKeyWrapper(const std::wstring& key_path, NTSTATUS CreateKeyWrapper(const std::wstring& key_path,
ACCESS_MASK access, ACCESS_MASK access,
HANDLE* out_handle, HANDLE* out_handle,
...@@ -217,10 +565,6 @@ NTSTATUS CreateKeyWrapper(const std::wstring& key_path, ...@@ -217,10 +565,6 @@ NTSTATUS CreateKeyWrapper(const std::wstring& key_path,
namespace nt { 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 // Create, open, delete, close functions
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
...@@ -229,13 +573,29 @@ bool CreateRegKey(ROOT_KEY root, ...@@ -229,13 +573,29 @@ bool CreateRegKey(ROOT_KEY root,
const wchar_t* key_path, const wchar_t* key_path,
ACCESS_MASK access, ACCESS_MASK access,
HANDLE* out_handle OPTIONAL) { 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) if (!g_initialized)
InitNativeRegApi(); 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::wstring current_path;
std::vector<std::wstring> subkeys; std::vector<std::wstring> subkeys;
if (!ParseFullRegPath(ConvertRootKey(root), key_path, &current_path, if (!ParseFullRegPath(ConvertRootKey(root), redirected_key_path,
&subkeys)) &current_path, &subkeys))
return false; return false;
// Open the base hive first. It should always exist already. // Open the base hive first. It should always exist already.
...@@ -304,6 +664,12 @@ bool OpenRegKey(ROOT_KEY root, ...@@ -304,6 +664,12 @@ bool OpenRegKey(ROOT_KEY root,
ACCESS_MASK access, ACCESS_MASK access,
HANDLE* out_handle, HANDLE* out_handle,
NTSTATUS* error_code OPTIONAL) { 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) if (!g_initialized)
InitNativeRegApi(); InitNativeRegApi();
...@@ -312,8 +678,16 @@ bool OpenRegKey(ROOT_KEY root, ...@@ -312,8 +678,16 @@ bool OpenRegKey(ROOT_KEY root,
OBJECT_ATTRIBUTES obj = {}; OBJECT_ATTRIBUTES obj = {};
*out_handle = INVALID_HANDLE_VALUE; *out_handle = INVALID_HANDLE_VALUE;
std::wstring full_path(ConvertRootKey(root)); if (root == nt::AUTO)
full_path.append(key_path); 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()); g_rtl_init_unicode_string(&key_path_uni, full_path.c_str());
InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL, InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
...@@ -345,10 +719,12 @@ bool DeleteRegKey(HANDLE key) { ...@@ -345,10 +719,12 @@ bool DeleteRegKey(HANDLE key) {
} }
// wrapper function // 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; 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; return false;
if (!DeleteRegKey(key)) { if (!DeleteRegKey(key)) {
...@@ -430,13 +806,13 @@ bool QueryRegValueDWORD(HANDLE key, ...@@ -430,13 +806,13 @@ bool QueryRegValueDWORD(HANDLE key,
// wrapper function // wrapper function
bool QueryRegValueDWORD(ROOT_KEY root, bool QueryRegValueDWORD(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
DWORD* out_dword) { DWORD* out_dword) {
HANDLE key = INVALID_HANDLE_VALUE; HANDLE key = INVALID_HANDLE_VALUE;
if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key, if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
NULL))
return false; return false;
if (!QueryRegValueDWORD(key, value_name, out_dword)) { if (!QueryRegValueDWORD(key, value_name, out_dword)) {
...@@ -468,13 +844,13 @@ bool QueryRegValueSZ(HANDLE key, ...@@ -468,13 +844,13 @@ bool QueryRegValueSZ(HANDLE key,
// wrapper function // wrapper function
bool QueryRegValueSZ(ROOT_KEY root, bool QueryRegValueSZ(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
std::wstring* out_sz) { std::wstring* out_sz) {
HANDLE key = INVALID_HANDLE_VALUE; HANDLE key = INVALID_HANDLE_VALUE;
if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key, if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
NULL))
return false; return false;
if (!QueryRegValueSZ(key, value_name, out_sz)) { if (!QueryRegValueSZ(key, value_name, out_sz)) {
...@@ -522,13 +898,13 @@ bool QueryRegValueMULTISZ(HANDLE key, ...@@ -522,13 +898,13 @@ bool QueryRegValueMULTISZ(HANDLE key,
// wrapper function // wrapper function
bool QueryRegValueMULTISZ(ROOT_KEY root, bool QueryRegValueMULTISZ(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
std::vector<std::wstring>* out_multi_sz) { std::vector<std::wstring>* out_multi_sz) {
HANDLE key = INVALID_HANDLE_VALUE; HANDLE key = INVALID_HANDLE_VALUE;
if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key, if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
NULL))
return false; return false;
if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) { if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) {
...@@ -574,12 +950,13 @@ bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) { ...@@ -574,12 +950,13 @@ bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) {
// wrapper function // wrapper function
bool SetRegValueDWORD(ROOT_KEY root, bool SetRegValueDWORD(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
DWORD value) { DWORD value) {
HANDLE key = INVALID_HANDLE_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; return false;
if (!SetRegValueDWORD(key, value_name, value)) { if (!SetRegValueDWORD(key, value_name, value)) {
...@@ -606,12 +983,13 @@ bool SetRegValueSZ(HANDLE key, ...@@ -606,12 +983,13 @@ bool SetRegValueSZ(HANDLE key,
// wrapper function // wrapper function
bool SetRegValueSZ(ROOT_KEY root, bool SetRegValueSZ(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
const std::wstring& value) { const std::wstring& value) {
HANDLE key = INVALID_HANDLE_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; return false;
if (!SetRegValueSZ(key, value_name, value)) { if (!SetRegValueSZ(key, value_name, value)) {
...@@ -655,12 +1033,13 @@ bool SetRegValueMULTISZ(HANDLE key, ...@@ -655,12 +1033,13 @@ bool SetRegValueMULTISZ(HANDLE key,
// wrapper function // wrapper function
bool SetRegValueMULTISZ(ROOT_KEY root, bool SetRegValueMULTISZ(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
const std::vector<std::wstring>& values) { const std::vector<std::wstring>& values) {
HANDLE key = INVALID_HANDLE_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; return false;
if (!SetRegValueMULTISZ(key, value_name, values)) { if (!SetRegValueMULTISZ(key, value_name, values)) {
...@@ -682,4 +1061,38 @@ const wchar_t* GetCurrentUserSidString() { ...@@ -682,4 +1061,38 @@ const wchar_t* GetCurrentUserSidString() {
return g_current_user_sid_string; 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 }; // namespace nt
...@@ -28,16 +28,27 @@ ...@@ -28,16 +28,27 @@
namespace nt { namespace nt {
// These globals are only used in test suites that use reg redirection // Windows registry maximum lengths (in chars). Not including null char.
// of HKLM and/or HKCU. // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724872(v=vs.85).aspx
extern const size_t g_kRegMaxPathLen; constexpr size_t g_kRegMaxPathLen = 255;
extern wchar_t HKLM_override[]; constexpr size_t g_kRegMaxValueName = 16383;
extern wchar_t HKCU_override[];
// AUTO will choose depending on system install or not. // AUTO will choose depending on system install or not.
// Use HKLM or HKCU to override. // Use HKLM or HKCU to override.
typedef enum _ROOT_KEY { AUTO = 0, HKLM, HKCU } ROOT_KEY; 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. // Create and/or open a registry key.
// - This function will recursively create multiple sub-keys if required for // - This function will recursively create multiple sub-keys if required for
// |key_path|. // |key_path|.
...@@ -66,7 +77,10 @@ bool DeleteRegKey(HANDLE key); ...@@ -66,7 +77,10 @@ bool DeleteRegKey(HANDLE key);
// Delete a registry key. // Delete a registry key.
// - WRAPPER: Function opens and closes the target key for caller. // - 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. // Close a registry key handle that was opened with CreateRegKey or OpenRegKey.
void CloseRegKey(HANDLE key); void CloseRegKey(HANDLE key);
...@@ -96,7 +110,9 @@ bool QueryRegValueDWORD(HANDLE key, ...@@ -96,7 +110,9 @@ bool QueryRegValueDWORD(HANDLE key,
// Query DWORD value. // Query DWORD value.
// - WRAPPER: Function opens and closes the target key for caller, and works // - WRAPPER: Function opens and closes the target key for caller, and works
// with DWORD data type. // with DWORD data type.
// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
bool QueryRegValueDWORD(ROOT_KEY root, bool QueryRegValueDWORD(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
DWORD* out_dword); DWORD* out_dword);
...@@ -112,7 +128,9 @@ bool QueryRegValueSZ(HANDLE key, ...@@ -112,7 +128,9 @@ bool QueryRegValueSZ(HANDLE key,
// Query SZ (string) value. // Query SZ (string) value.
// - WRAPPER: Function opens and closes the target key for caller, and works // - WRAPPER: Function opens and closes the target key for caller, and works
// with SZ data type. // with SZ data type.
// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
bool QueryRegValueSZ(ROOT_KEY root, bool QueryRegValueSZ(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
std::wstring* out_sz); std::wstring* out_sz);
...@@ -128,7 +146,9 @@ bool QueryRegValueMULTISZ(HANDLE key, ...@@ -128,7 +146,9 @@ bool QueryRegValueMULTISZ(HANDLE key,
// Query MULTI_SZ (multiple strings) value. // Query MULTI_SZ (multiple strings) value.
// - WRAPPER: Function opens and closes the target key for caller, and works // - WRAPPER: Function opens and closes the target key for caller, and works
// with MULTI_SZ data type. // with MULTI_SZ data type.
// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
bool QueryRegValueMULTISZ(ROOT_KEY root, bool QueryRegValueMULTISZ(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
std::vector<std::wstring>* out_multi_sz); std::vector<std::wstring>* out_multi_sz);
...@@ -155,7 +175,9 @@ bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value); ...@@ -155,7 +175,9 @@ bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value);
// Set DWORD value. // Set DWORD value.
// - WRAPPER: Function opens and closes the target key for caller, and works // - WRAPPER: Function opens and closes the target key for caller, and works
// with DWORD data type. // with DWORD data type.
// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
bool SetRegValueDWORD(ROOT_KEY root, bool SetRegValueDWORD(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
DWORD value); DWORD value);
...@@ -171,7 +193,9 @@ bool SetRegValueSZ(HANDLE key, ...@@ -171,7 +193,9 @@ bool SetRegValueSZ(HANDLE key,
// Set SZ (string) value. // Set SZ (string) value.
// - WRAPPER: Function opens and closes the target key for caller, and works // - WRAPPER: Function opens and closes the target key for caller, and works
// with SZ data type. // with SZ data type.
// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
bool SetRegValueSZ(ROOT_KEY root, bool SetRegValueSZ(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
const std::wstring& value); const std::wstring& value);
...@@ -187,7 +211,9 @@ bool SetRegValueMULTISZ(HANDLE key, ...@@ -187,7 +211,9 @@ bool SetRegValueMULTISZ(HANDLE key,
// Set MULTI_SZ (multiple strings) value. // Set MULTI_SZ (multiple strings) value.
// - WRAPPER: Function opens and closes the target key for caller, and works // - WRAPPER: Function opens and closes the target key for caller, and works
// with MULTI_SZ data type. // with MULTI_SZ data type.
// - Use |wow64_override| to force redirection behaviour, or pass nt::NONE.
bool SetRegValueMULTISZ(ROOT_KEY root, bool SetRegValueMULTISZ(ROOT_KEY root,
WOW64_OVERRIDE wow64_override,
const wchar_t* key_path, const wchar_t* key_path,
const wchar_t* value_name, const wchar_t* value_name,
const std::vector<std::wstring>& values); const std::vector<std::wstring>& values);
...@@ -199,6 +225,15 @@ bool SetRegValueMULTISZ(ROOT_KEY root, ...@@ -199,6 +225,15 @@ bool SetRegValueMULTISZ(ROOT_KEY root,
// Returns the current user SID in string form. // Returns the current user SID in string form.
const wchar_t* GetCurrentUserSidString(); 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 }; // namespace nt
#endif // CHROME_ELF_NT_REGISTRY_NT_REGISTRY_H_ #endif // CHROME_ELF_NT_REGISTRY_NT_REGISTRY_H_
// 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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment