1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/chrome_elf/nt_registry/nt_registry.h"
6
7 #include <assert.h>
8 #include <stdlib.h>
9
10 #include <memory>
11 #include <string>
12
13 namespace {
14
15 // Function pointers used for registry access.
16 RtlInitUnicodeStringFunction g_rtl_init_unicode_string = nullptr;
17 NtCreateKeyFunction g_nt_create_key = nullptr;
18 NtDeleteKeyFunction g_nt_delete_key = nullptr;
19 NtOpenKeyExFunction g_nt_open_key_ex = nullptr;
20 NtCloseFunction g_nt_close = nullptr;
21 NtQueryKeyFunction g_nt_query_key = nullptr;
22 NtEnumerateKeyFunction g_nt_enumerate_key = nullptr;
23 NtQueryValueKeyFunction g_nt_query_value_key = nullptr;
24 NtSetValueKeyFunction g_nt_set_value_key = nullptr;
25
26 // Lazy init. No concern about concurrency in chrome_elf.
27 bool g_initialized = false;
28 bool g_system_install = false;
29 bool g_wow64_proc = false;
30 wchar_t g_kRegPathHKLM[] = L"\\Registry\\Machine\\";
31 wchar_t g_kRegPathHKCU[nt::g_kRegMaxPathLen + 1] = L"";
32 wchar_t g_current_user_sid_string[nt::g_kRegMaxPathLen + 1] = L"";
33
34 // Max number of tries for system API calls when STATUS_BUFFER_OVERFLOW or
35 // STATUS_BUFFER_TOO_SMALL can be returned.
36 enum { kMaxTries = 5 };
37
38 // For testing only.
39 wchar_t g_HKLM_override[nt::g_kRegMaxPathLen + 1] = L"";
40 wchar_t g_HKCU_override[nt::g_kRegMaxPathLen + 1] = L"";
41
42 //------------------------------------------------------------------------------
43 // Initialization - LOCAL
44 //------------------------------------------------------------------------------
45
46 // Not using install_static, to prevent circular dependency.
IsThisProcSystem()47 bool IsThisProcSystem() {
48 wchar_t program_dir[MAX_PATH] = {};
49 wchar_t* cmd_line = GetCommandLineW();
50 // If our command line starts with the "Program Files" or
51 // "Program Files (x86)" path, we're system.
52 DWORD ret = ::GetEnvironmentVariable(L"PROGRAMFILES", program_dir, MAX_PATH);
53 if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
54 return true;
55
56 ret = ::GetEnvironmentVariable(L"PROGRAMFILES(X86)", program_dir, MAX_PATH);
57 if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
58 return true;
59
60 return false;
61 }
62
IsThisProcWow64()63 bool IsThisProcWow64() {
64 // Using BOOL type for compat with IsWow64Process() system API.
65 BOOL is_wow64 = FALSE;
66
67 // API might not exist, so dynamic lookup.
68 using IsWow64ProcessFunction = decltype(&IsWow64Process);
69 IsWow64ProcessFunction is_wow64_process =
70 reinterpret_cast<IsWow64ProcessFunction>(::GetProcAddress(
71 ::GetModuleHandle(L"kernel32.dll"), "IsWow64Process"));
72 if (!is_wow64_process)
73 return false;
74 if (!is_wow64_process(::GetCurrentProcess(), &is_wow64))
75 return false;
76 return is_wow64 ? true : false;
77 }
78
InitNativeRegApi()79 bool InitNativeRegApi() {
80 HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
81
82 // Setup the global function pointers for registry access.
83 g_rtl_init_unicode_string = reinterpret_cast<RtlInitUnicodeStringFunction>(
84 ::GetProcAddress(ntdll, "RtlInitUnicodeString"));
85
86 g_nt_create_key = reinterpret_cast<NtCreateKeyFunction>(
87 ::GetProcAddress(ntdll, "NtCreateKey"));
88
89 g_nt_delete_key = reinterpret_cast<NtDeleteKeyFunction>(
90 ::GetProcAddress(ntdll, "NtDeleteKey"));
91
92 g_nt_open_key_ex = reinterpret_cast<NtOpenKeyExFunction>(
93 ::GetProcAddress(ntdll, "NtOpenKeyEx"));
94
95 g_nt_close =
96 reinterpret_cast<NtCloseFunction>(::GetProcAddress(ntdll, "NtClose"));
97
98 g_nt_query_key = reinterpret_cast<NtQueryKeyFunction>(
99 ::GetProcAddress(ntdll, "NtQueryKey"));
100
101 g_nt_enumerate_key = reinterpret_cast<NtEnumerateKeyFunction>(
102 ::GetProcAddress(ntdll, "NtEnumerateKey"));
103
104 g_nt_query_value_key = reinterpret_cast<NtQueryValueKeyFunction>(
105 ::GetProcAddress(ntdll, "NtQueryValueKey"));
106
107 g_nt_set_value_key = reinterpret_cast<NtSetValueKeyFunction>(
108 ::GetProcAddress(ntdll, "NtSetValueKey"));
109
110 if (!g_rtl_init_unicode_string || !g_nt_create_key || !g_nt_open_key_ex ||
111 !g_nt_delete_key || !g_nt_close || !g_nt_query_key ||
112 !g_nt_enumerate_key || !g_nt_query_value_key || !g_nt_set_value_key)
113 return false;
114
115 // We need to set HKCU based on the sid of the current user account.
116 RtlFormatCurrentUserKeyPathFunction rtl_current_user_string =
117 reinterpret_cast<RtlFormatCurrentUserKeyPathFunction>(
118 ::GetProcAddress(ntdll, "RtlFormatCurrentUserKeyPath"));
119
120 RtlFreeUnicodeStringFunction rtl_free_unicode_str =
121 reinterpret_cast<RtlFreeUnicodeStringFunction>(
122 ::GetProcAddress(ntdll, "RtlFreeUnicodeString"));
123
124 if (!rtl_current_user_string || !rtl_free_unicode_str)
125 return false;
126
127 UNICODE_STRING current_user_reg_path;
128 if (!NT_SUCCESS(rtl_current_user_string(¤t_user_reg_path)))
129 return false;
130
131 // Finish setting up global HKCU path.
132 ::wcsncat(g_kRegPathHKCU, current_user_reg_path.Buffer, nt::g_kRegMaxPathLen);
133 ::wcsncat(g_kRegPathHKCU, L"\\",
134 (nt::g_kRegMaxPathLen - ::wcslen(g_kRegPathHKCU)));
135 // Keep the sid string as well.
136 wchar_t* ptr = ::wcsrchr(current_user_reg_path.Buffer, L'\\');
137 ptr++;
138 ::wcsncpy(g_current_user_sid_string, ptr, nt::g_kRegMaxPathLen);
139 rtl_free_unicode_str(¤t_user_reg_path);
140
141 // Figure out if this is a system or user install.
142 g_system_install = IsThisProcSystem();
143
144 // Figure out if this is a WOW64 process.
145 g_wow64_proc = IsThisProcWow64();
146
147 g_initialized = true;
148 return true;
149 }
150
151 //------------------------------------------------------------------------------
152 // Reg WOW64 Redirection - LOCAL
153 //
154 // How registry redirection works directly calling NTDLL APIs:
155 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
156 // - NOTE: On >= Win7, reflection support was removed.
157 // -
158 // https://msdn.microsoft.com/en-us/library/windows/desktop/aa384253(v=vs.85).aspx
159 //
160 // - 1) 32-bit / WOW64 process:
161 // a) Default access WILL be redirected to WOW64.
162 // b) KEY_WOW64_32KEY access WILL be redirected to WOW64.
163 // c) KEY_WOW64_64KEY access will NOT be redirected to WOW64.
164 //
165 // - 2) 64-bit process:
166 // a) Default access will NOT be redirected to WOW64.
167 // b) KEY_WOW64_32KEY access will NOT be redirected to WOW64.
168 // c) KEY_WOW64_64KEY access will NOT be redirected to WOW64.
169 //
170 // - Key point from above is that NTDLL redirects and respects access
171 // overrides for WOW64 calling processes. But does NOT do any of that if the
172 // calling process is 64-bit. 2b is surprising and troublesome.
173 //
174 // How registry redirection works using these nt_registry APIs:
175 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
176 // - These APIs will behave the same as NTDLL above, EXCEPT for 2b.
177 // nt_registry APIs will respect the override access flags for all processes.
178 //
179 // - How the WOW64 redirection decision trees / Nodes work below:
180 //
181 // The HKLM and HKCU decision trees represent the information at the MSDN
182 // link above... but in a way that generates a decision about whether a
183 // registry path should be subject to WOW64 redirection. The tree is
184 // traversed as you scan along the registry path in question.
185 //
186 // - Each Node contains a chunk of registry subkey(s) to match.
187 // - If it is NOT matched, traversal is done.
188 // - If it is matched:
189 // - Current state of |redirection_type| for the whole registry path is
190 // updated.
191 // - If |next| is empty, traversal is done.
192 // - Otherwise, |next| is an array of child Nodes to try to match against.
193 // Loop.
194 //------------------------------------------------------------------------------
195
196 // This enum defines states for how to handle redirection.
197 // NOTE: When WOW64 redirection should happen, the redirect subkey can be either
198 // before or after the latest Node match. Unfortunately not consistent.
199 enum RedirectionType { SHARED = 0, REDIRECTED_BEFORE, REDIRECTED_AFTER };
200
201 struct Node {
202 template <size_t len, size_t n_len>
Node__anon2330ca6c0111::Node203 constexpr Node(const wchar_t (&wcs)[len],
204 RedirectionType rt,
205 const Node (&n)[n_len])
206 : to_match(wcs),
207 to_match_len(len - 1),
208 redirection_type(rt),
209 next(n),
210 next_len(n_len) {}
211
212 template <size_t len>
Node__anon2330ca6c0111::Node213 constexpr Node(const wchar_t (&wcs)[len], RedirectionType rt)
214 : to_match(wcs),
215 to_match_len(len - 1),
216 redirection_type(rt),
217 next(nullptr),
218 next_len(0) {}
219
220 const wchar_t* to_match;
221 size_t to_match_len;
222 // If a match, this is the new state of how to redirect.
223 RedirectionType redirection_type;
224 // |next| is nullptr or an array of Nodes of length |array_len|.
225 const Node* next;
226 size_t next_len;
227 };
228
229 // HKLM or HKCU SOFTWARE\Classes is shared by default. Specific subkeys under
230 // Classes are redirected to SOFTWARE\WOW6432Node\Classes\<subkey> though.
231 constexpr Node kClassesSubtree[] = {{L"CLSID", REDIRECTED_BEFORE},
232 {L"DirectShow", REDIRECTED_BEFORE},
233 {L"Interface", REDIRECTED_BEFORE},
234 {L"Media Type", REDIRECTED_BEFORE},
235 {L"MediaFoundation", REDIRECTED_BEFORE}};
236
237 // These specific HKLM\SOFTWARE subkeys are shared. Specific
238 // subkeys under Classes are redirected though... see classes_subtree.
239 constexpr Node kHklmSoftwareSubtree[] = {
240 // TODO(pennymac): when MS fixes compiler bug, or bots are all using clang,
241 // remove the "Classes" subkeys below and replace with:
242 // {L"Classes", SHARED, kClassesSubtree},
243 // https://connect.microsoft.com/VisualStudio/feedback/details/3104499
244 {L"Classes\\CLSID", REDIRECTED_BEFORE},
245 {L"Classes\\DirectShow", REDIRECTED_BEFORE},
246 {L"Classes\\Interface", REDIRECTED_BEFORE},
247 {L"Classes\\Media Type", REDIRECTED_BEFORE},
248 {L"Classes\\MediaFoundation", REDIRECTED_BEFORE},
249 {L"Classes", SHARED},
250
251 {L"Clients", SHARED},
252 {L"Microsoft\\COM3", SHARED},
253 {L"Microsoft\\Cryptography\\Calais\\Current", SHARED},
254 {L"Microsoft\\Cryptography\\Calais\\Readers", SHARED},
255 {L"Microsoft\\Cryptography\\Services", SHARED},
256
257 {L"Microsoft\\CTF\\SystemShared", SHARED},
258 {L"Microsoft\\CTF\\TIP", SHARED},
259 {L"Microsoft\\DFS", SHARED},
260 {L"Microsoft\\Driver Signing", SHARED},
261 {L"Microsoft\\EnterpriseCertificates", SHARED},
262
263 {L"Microsoft\\EventSystem", SHARED},
264 {L"Microsoft\\MSMQ", SHARED},
265 {L"Microsoft\\Non-Driver Signing", SHARED},
266 {L"Microsoft\\Notepad\\DefaultFonts", SHARED},
267 {L"Microsoft\\OLE", SHARED},
268
269 {L"Microsoft\\RAS", SHARED},
270 {L"Microsoft\\RPC", SHARED},
271 {L"Microsoft\\SOFTWARE\\Microsoft\\Shared Tools\\MSInfo", SHARED},
272 {L"Microsoft\\SystemCertificates", SHARED},
273 {L"Microsoft\\TermServLicensing", SHARED},
274
275 {L"Microsoft\\Transaction Server", SHARED},
276 {L"Microsoft\\Windows\\CurrentVersion\\App Paths", SHARED},
277 {L"Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cursors\\Schemes",
278 SHARED},
279 {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers", SHARED},
280 {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\DriveIcons", SHARED},
281
282 {L"Microsoft\\Windows\\CurrentVersion\\Explorer\\KindMap", SHARED},
283 {L"Microsoft\\Windows\\CurrentVersion\\Group Policy", SHARED},
284 {L"Microsoft\\Windows\\CurrentVersion\\Policies", SHARED},
285 {L"Microsoft\\Windows\\CurrentVersion\\PreviewHandlers", SHARED},
286 {L"Microsoft\\Windows\\CurrentVersion\\Setup", SHARED},
287
288 {L"Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations", SHARED},
289 {L"Microsoft\\Windows NT\\CurrentVersion\\Console", SHARED},
290 {L"Microsoft\\Windows NT\\CurrentVersion\\FontDpi", SHARED},
291 {L"Microsoft\\Windows NT\\CurrentVersion\\FontLink", SHARED},
292 {L"Microsoft\\Windows NT\\CurrentVersion\\FontMapper", SHARED},
293
294 {L"Microsoft\\Windows NT\\CurrentVersion\\Fonts", SHARED},
295 {L"Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes", SHARED},
296 {L"Microsoft\\Windows NT\\CurrentVersion\\Gre_Initialize", SHARED},
297 {L"Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options",
298 SHARED},
299 {L"Microsoft\\Windows NT\\CurrentVersion\\LanguagePack", SHARED},
300
301 {L"Microsoft\\Windows NT\\CurrentVersion\\NetworkCards", SHARED},
302 {L"Microsoft\\Windows NT\\CurrentVersion\\Perflib", SHARED},
303 {L"Microsoft\\Windows NT\\CurrentVersion\\Ports", SHARED},
304 {L"Microsoft\\Windows NT\\CurrentVersion\\Print", SHARED},
305 {L"Microsoft\\Windows NT\\CurrentVersion\\ProfileList", SHARED},
306
307 {L"Microsoft\\Windows NT\\CurrentVersion\\Time Zones", SHARED},
308 {L"Policies", SHARED},
309 {L"RegisteredApplications", SHARED}};
310
311 // HKCU is entirely shared, except for a few specific Classes subkeys which
312 // are redirected. See |classes_subtree|.
313 constexpr Node kRedirectionDecisionTreeHkcu = {L"SOFTWARE\\Classes", SHARED,
314 kClassesSubtree};
315
316 // HKLM\SOFTWARE is redirected by default to SOFTWARE\WOW6432Node. Specific
317 // subkeys under SOFTWARE are shared though... see |hklm_software_subtree|.
318 constexpr Node kRedirectionDecisionTreeHklm = {L"SOFTWARE", REDIRECTED_AFTER,
319 kHklmSoftwareSubtree};
320
321 // Main redirection handler function.
322 // If redirection is required, change is made to |subkey_path| in place.
323 //
324 // - This function should be called BEFORE concatenating |subkey_path| with the
325 // root hive or calling ParseFullRegPath().
326 // - Also, |subkey_path| should be passed to SanitizeSubkeyPath() before calling
327 // this function.
ProcessRedirection(nt::ROOT_KEY root,ACCESS_MASK access,std::wstring * subkey_path)328 void ProcessRedirection(nt::ROOT_KEY root,
329 ACCESS_MASK access,
330 std::wstring* subkey_path) {
331 static constexpr wchar_t kRedirectBefore[] = L"WOW6432Node\\";
332 static constexpr wchar_t kRedirectAfter[] = L"\\WOW6432Node";
333
334 assert(subkey_path);
335 assert(subkey_path->empty() || subkey_path->front() != L'\\');
336 assert(subkey_path->empty() || subkey_path->back() != L'\\');
337 assert(root != nt::AUTO);
338
339 // |subkey_path| could legitimately be empty.
340 if (subkey_path->empty() ||
341 (access & KEY_WOW64_32KEY && access & KEY_WOW64_64KEY))
342 return;
343
344 // No redirection during testing when there's already an override.
345 // Otherwise, the testing redirect directory Software\Chromium\TempTestKeys
346 // would get WOW64 redirected if root_key == HKLM in this function.
347 if (root == nt::HKCU ? *g_HKCU_override : *g_HKLM_override)
348 return;
349
350 // WOW64 redirection only supported on x64 architecture. Return if x86.
351 SYSTEM_INFO system_info = {};
352 ::GetNativeSystemInfo(&system_info);
353 if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL)
354 return;
355
356 bool use_wow64 = g_wow64_proc;
357 // Consider KEY_WOW64_32KEY and KEY_WOW64_64KEY override access flags.
358 if (access & KEY_WOW64_32KEY)
359 use_wow64 = true;
360 if (access & KEY_WOW64_64KEY)
361 use_wow64 = false;
362
363 // If !use_wow64, there's nothing more to do.
364 if (!use_wow64)
365 return;
366
367 // The root of the decision trees are an array of 1.
368 size_t node_array_len = 1;
369 // Pick which decision tree to use.
370 const Node* current_node = (root == nt::HKCU) ? &kRedirectionDecisionTreeHkcu
371 : &kRedirectionDecisionTreeHklm;
372
373 // The following loop works on the |subkey_path| from left to right.
374 // |position| tracks progress along |subkey_path|.
375 const wchar_t* position = subkey_path->c_str();
376 // Hold a count of chars left after position, for efficient calculations.
377 size_t chars_left = subkey_path->length();
378 // |redirect_state| holds the latest state of redirection requirement.
379 RedirectionType redirect_state = SHARED;
380 // |insertion_point| tracks latest spot for redirection subkey to be inserted.
381 const wchar_t* insertion_point = nullptr;
382 // |insert_string| tracks which redirection string would be inserted.
383 const wchar_t* insert_string = nullptr;
384
385 size_t node_index = 0;
386 while (node_index < node_array_len) {
387 size_t current_to_match_len = current_node->to_match_len;
388 // Make sure the remainder of the path is at least as long as the current
389 // subkey to match.
390 if (chars_left >= current_to_match_len) {
391 // Do case insensitive comparisons.
392 if (!::wcsnicmp(position, current_node->to_match, current_to_match_len)) {
393 // Make sure not to match on a substring.
394 if (*(position + current_to_match_len) == L'\\' ||
395 *(position + current_to_match_len) == L'\0') {
396 // MATCH!
397 // -------------------------------------------------------------------
398 // 1) Update state of redirection.
399 redirect_state = current_node->redirection_type;
400 // 1.5) If new state is to redirect, the new insertion point will be
401 // either right before or right after this match.
402 if (redirect_state == REDIRECTED_BEFORE) {
403 insertion_point = position;
404 insert_string = kRedirectBefore;
405 } else if (redirect_state == REDIRECTED_AFTER) {
406 insertion_point = position + current_to_match_len;
407 insert_string = kRedirectAfter;
408 }
409 // 2) Adjust |position| along the subkey path.
410 position += current_to_match_len;
411 chars_left -= current_to_match_len;
412 // 2.5) Increment the position, to move past path seperator(s).
413 while (*position == L'\\') {
414 ++position;
415 --chars_left;
416 }
417 // 3) Move our loop parameters to the |next| array of Nodes.
418 node_array_len = current_node->next_len;
419 current_node = current_node->next;
420 node_index = 0;
421 // 4) Finish this loop and start on new array.
422 continue;
423 }
424 }
425 }
426
427 // Move to the next node in the array if we didn't match this loop.
428 ++current_node;
429 ++node_index;
430 }
431
432 if (redirect_state == SHARED)
433 return;
434
435 // Insert the redirection into |subkey_path|, at |insertion_point|.
436 subkey_path->insert((insertion_point - subkey_path->c_str()), insert_string);
437 }
438
439 //------------------------------------------------------------------------------
440 // Reg Path Utilities - LOCAL
441 //------------------------------------------------------------------------------
442
ConvertRootKey(nt::ROOT_KEY root)443 std::wstring ConvertRootKey(nt::ROOT_KEY root) {
444 assert(root != nt::AUTO);
445
446 if (root == nt::HKCU && *g_HKCU_override) {
447 std::wstring temp = g_kRegPathHKCU;
448 temp.append(g_HKCU_override);
449 temp.append(L"\\");
450 return temp;
451 } else if (root == nt::HKLM && *g_HKLM_override) {
452 // Yes, HKLM override goes into HKCU. This is not a typo.
453 std::wstring temp = g_kRegPathHKCU;
454 temp.append(g_HKLM_override);
455 temp.append(L"\\");
456 return temp;
457 }
458
459 return (root == nt::HKCU) ? g_kRegPathHKCU : g_kRegPathHKLM;
460 }
461
462 // This utility should be called on an externally provided subkey path.
463 // - Ensures there are no starting or trailing backslashes, and no more than
464 // - one backslash in a row.
465 // - Note from MSDN: "Key names cannot include the backslash character (\),
466 // but any other printable character can be used. Value names and data can
467 // include the backslash character."
SanitizeSubkeyPath(std::wstring * input)468 void SanitizeSubkeyPath(std::wstring* input) {
469 assert(input);
470
471 // Remove trailing backslashes.
472 size_t last_valid_pos = input->find_last_not_of(L'\\');
473 if (last_valid_pos == std::wstring::npos) {
474 // The string is all backslashes, or it's empty. Clear and abort.
475 input->clear();
476 return;
477 }
478 // Chop off the trailing backslashes.
479 input->resize(last_valid_pos + 1);
480
481 // Remove leading backslashes.
482 input->erase(0, input->find_first_not_of(L'\\'));
483
484 // Replace any occurances of more than 1 backslash in a row with just 1.
485 size_t index = input->find_first_of(L"\\");
486 while (index != std::wstring::npos) {
487 // Remove a second consecutive backslash, and leave index where it is,
488 // or move to the next backslash in the string.
489 if ((*input)[index + 1] == L'\\')
490 input->erase(index + 1, 1);
491 else
492 index = input->find_first_of(L"\\", index + 1);
493 }
494 }
495
496 // Turns a root and subkey path into the registry base hive and the rest of the
497 // subkey tokens.
498 // - |converted_root| should come directly out of ConvertRootKey function.
499 // - |subkey_path| should be passed to SanitizeSubkeyPath() first.
500 // - E.g. base hive: "\Registry\Machine\", "\Registry\User\<SID>\".
ParseFullRegPath(const std::wstring & converted_root,const std::wstring & subkey_path,std::wstring * out_base,std::vector<std::wstring> * subkeys)501 bool ParseFullRegPath(const std::wstring& converted_root,
502 const std::wstring& subkey_path,
503 std::wstring* out_base,
504 std::vector<std::wstring>* subkeys) {
505 out_base->clear();
506 subkeys->clear();
507 std::wstring temp_path;
508
509 // Special case if there is testing redirection set up.
510 if (*g_HKCU_override || *g_HKLM_override) {
511 // Why process |converted_root|? To handle reg redirection used by tests.
512 // E.g.:
513 // |converted_root| = "\REGISTRY\USER\S-1-5-21-39260824-743453154-142223018-
514 // 716772\Software\Chromium\TempTestKeys\13110669370890870$94c6ed9d-bc34-
515 // 44f3-a0b3-9eee2d3f2f82\".
516 // |subkey_path| = "SOFTWARE\Google\Chrome\BrowserSec".
517 //
518 // Note: bypassing the starting backslash in the |converted_root|.
519 temp_path.append(converted_root, 1, converted_root.size() - 1);
520 }
521 temp_path.append(subkey_path);
522
523 // Tokenize the full path.
524 size_t find_start = 0;
525 size_t delimiter = temp_path.find_first_of(L'\\');
526 while (delimiter != std::wstring::npos) {
527 subkeys->emplace_back(temp_path, find_start, delimiter - find_start);
528 // Move past the backslash.
529 find_start = delimiter + 1;
530 delimiter = temp_path.find_first_of(L'\\', find_start);
531 }
532 // Get the last token if there is one.
533 if (!temp_path.empty())
534 subkeys->emplace_back(temp_path, find_start);
535
536 // Special case if there is testing redirection set up.
537 if (*g_HKCU_override || *g_HKLM_override) {
538 // The base hive for HKCU needs to include the user SID.
539 uint32_t num_base_tokens = 2;
540 if (0 == temp_path.compare(0, 14, L"REGISTRY\\USER\\"))
541 num_base_tokens = 3;
542
543 if (subkeys->size() < num_base_tokens)
544 return false;
545
546 // Pull out the base hive tokens.
547 out_base->push_back(L'\\');
548 for (size_t i = 0; i < num_base_tokens; ++i) {
549 out_base->append((*subkeys)[i]);
550 out_base->push_back(L'\\');
551 }
552 subkeys->erase(subkeys->begin(), subkeys->begin() + num_base_tokens);
553 } else {
554 out_base->assign(converted_root);
555 }
556
557 return true;
558 }
559
560 // String safety.
561 // - NOTE: only working with wchar_t here.
562 // - Also ensures the content of |value_bytes| is at least a terminator.
563 // - Pass "true" for |multi| for MULTISZ.
EnsureTerminatedSZ(std::vector<BYTE> * value_bytes,bool multi)564 void EnsureTerminatedSZ(std::vector<BYTE>* value_bytes, bool multi) {
565 DWORD terminator_size = sizeof(wchar_t);
566
567 if (multi)
568 terminator_size = 2 * sizeof(wchar_t);
569
570 // Ensure content is at least the size of a terminator.
571 if (value_bytes->size() < terminator_size) {
572 value_bytes->insert(value_bytes->end(),
573 terminator_size - value_bytes->size(), 0);
574 }
575
576 // Sanity check content size based on character size.
577 DWORD modulo = value_bytes->size() % sizeof(wchar_t);
578 value_bytes->insert(value_bytes->end(), modulo, 0);
579
580 // Now finally check for trailing terminator.
581 bool terminated = true;
582 size_t last_element = value_bytes->size() - 1;
583 for (size_t i = 0; i < terminator_size; i++) {
584 if ((*value_bytes)[last_element - i] != 0) {
585 terminated = false;
586 break;
587 }
588 }
589
590 if (terminated)
591 return;
592
593 // Append a full terminator to be safe.
594 value_bytes->insert(value_bytes->end(), terminator_size, 0);
595
596 return;
597 }
598
599 //------------------------------------------------------------------------------
600 // Misc wrapper functions - LOCAL
601 //------------------------------------------------------------------------------
602
CreateKeyWrapper(const std::wstring & key_path,ACCESS_MASK access,HANDLE * out_handle,ULONG * create_or_open OPTIONAL)603 NTSTATUS CreateKeyWrapper(const std::wstring& key_path,
604 ACCESS_MASK access,
605 HANDLE* out_handle,
606 ULONG* create_or_open OPTIONAL) {
607 UNICODE_STRING key_path_uni = {};
608 g_rtl_init_unicode_string(&key_path_uni, key_path.c_str());
609
610 OBJECT_ATTRIBUTES obj = {};
611 InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
612 nullptr);
613
614 return g_nt_create_key(out_handle, access, &obj, 0, nullptr,
615 REG_OPTION_NON_VOLATILE, create_or_open);
616 }
617
618 } // namespace
619
620 namespace nt {
621
622 //------------------------------------------------------------------------------
623 // Create, open, delete, close functions
624 //------------------------------------------------------------------------------
625
CreateRegKey(ROOT_KEY root,const wchar_t * key_path,ACCESS_MASK access,HANDLE * out_handle OPTIONAL)626 bool CreateRegKey(ROOT_KEY root,
627 const wchar_t* key_path,
628 ACCESS_MASK access,
629 HANDLE* out_handle OPTIONAL) {
630 // |key_path| can be null or empty, but it can't be longer than
631 // |g_kRegMaxPathLen| at this point.
632 if (key_path &&
633 ::wcsnlen(key_path, g_kRegMaxPathLen + 1) == g_kRegMaxPathLen + 1)
634 return false;
635
636 if (!g_initialized && !InitNativeRegApi())
637 return false;
638
639 if (root == nt::AUTO)
640 root = g_system_install ? nt::HKLM : nt::HKCU;
641
642 std::wstring redirected_key_path;
643 if (key_path) {
644 redirected_key_path = key_path;
645 SanitizeSubkeyPath(&redirected_key_path);
646 ProcessRedirection(root, access, &redirected_key_path);
647 }
648
649 std::wstring current_path;
650 std::vector<std::wstring> subkeys;
651 if (!ParseFullRegPath(ConvertRootKey(root), redirected_key_path,
652 ¤t_path, &subkeys))
653 return false;
654
655 // Open the base hive first. It should always exist already.
656 HANDLE last_handle = INVALID_HANDLE_VALUE;
657 NTSTATUS status =
658 CreateKeyWrapper(current_path, access, &last_handle, nullptr);
659 if (!NT_SUCCESS(status))
660 return false;
661
662 size_t subkeys_size = subkeys.size();
663 if (subkeys_size != 0)
664 g_nt_close(last_handle);
665
666 // Recursively open/create each subkey.
667 std::vector<HANDLE> rollback;
668 bool success = true;
669 for (size_t i = 0; i < subkeys_size; i++) {
670 current_path.append(subkeys[i]);
671 current_path.push_back(L'\\');
672
673 // Process the latest subkey.
674 ULONG created = 0;
675 HANDLE key_handle = INVALID_HANDLE_VALUE;
676 status =
677 CreateKeyWrapper(current_path.c_str(), access, &key_handle, &created);
678 if (!NT_SUCCESS(status)) {
679 success = false;
680 break;
681 }
682
683 if (i == subkeys_size - 1) {
684 last_handle = key_handle;
685 } else {
686 // Save any subkey handle created, in case of rollback.
687 if (created == REG_CREATED_NEW_KEY)
688 rollback.push_back(key_handle);
689 else
690 g_nt_close(key_handle);
691 }
692 }
693
694 if (!success) {
695 // Delete any subkeys created.
696 for (HANDLE handle : rollback) {
697 g_nt_delete_key(handle);
698 }
699 }
700 for (HANDLE handle : rollback) {
701 // Close the rollback handles, on success or failure.
702 g_nt_close(handle);
703 }
704 if (!success)
705 return false;
706
707 // See if caller wants the handle left open.
708 if (out_handle)
709 *out_handle = last_handle;
710 else
711 g_nt_close(last_handle);
712
713 return true;
714 }
715
OpenRegKey(ROOT_KEY root,const wchar_t * key_path,ACCESS_MASK access,HANDLE * out_handle,NTSTATUS * error_code OPTIONAL)716 bool OpenRegKey(ROOT_KEY root,
717 const wchar_t* key_path,
718 ACCESS_MASK access,
719 HANDLE* out_handle,
720 NTSTATUS* error_code OPTIONAL) {
721 // |key_path| can be null or empty, but it can't be longer than
722 // |g_kRegMaxPathLen| at this point.
723 if (key_path &&
724 ::wcsnlen(key_path, g_kRegMaxPathLen + 1) == g_kRegMaxPathLen + 1)
725 return false;
726
727 if (!g_initialized && !InitNativeRegApi())
728 return false;
729
730 NTSTATUS status = STATUS_UNSUCCESSFUL;
731 UNICODE_STRING key_path_uni = {};
732 OBJECT_ATTRIBUTES obj = {};
733 *out_handle = INVALID_HANDLE_VALUE;
734
735 if (root == nt::AUTO)
736 root = g_system_install ? nt::HKLM : nt::HKCU;
737
738 std::wstring full_path;
739 if (key_path) {
740 full_path = key_path;
741 SanitizeSubkeyPath(&full_path);
742 ProcessRedirection(root, access, &full_path);
743 }
744 full_path.insert(0, ConvertRootKey(root));
745
746 g_rtl_init_unicode_string(&key_path_uni, full_path.c_str());
747 InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
748 NULL);
749
750 status = g_nt_open_key_ex(out_handle, access, &obj, 0);
751 // See if caller wants the NTSTATUS.
752 if (error_code)
753 *error_code = status;
754
755 if (NT_SUCCESS(status))
756 return true;
757
758 return false;
759 }
760
DeleteRegKey(HANDLE key)761 bool DeleteRegKey(HANDLE key) {
762 if (!g_initialized && !InitNativeRegApi())
763 return false;
764
765 NTSTATUS status = g_nt_delete_key(key);
766
767 return NT_SUCCESS(status);
768 }
769
770 // wrapper function
DeleteRegKey(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path)771 bool DeleteRegKey(ROOT_KEY root,
772 WOW64_OVERRIDE wow64_override,
773 const wchar_t* key_path) {
774 HANDLE key = INVALID_HANDLE_VALUE;
775
776 if (!OpenRegKey(root, key_path, DELETE | wow64_override, &key, nullptr))
777 return false;
778
779 if (!DeleteRegKey(key)) {
780 CloseRegKey(key);
781 return false;
782 }
783
784 CloseRegKey(key);
785 return true;
786 }
787
CloseRegKey(HANDLE key)788 void CloseRegKey(HANDLE key) {
789 if (!g_initialized)
790 InitNativeRegApi();
791 g_nt_close(key);
792 }
793
794 //------------------------------------------------------------------------------
795 // Getter functions
796 //------------------------------------------------------------------------------
797
QueryRegKeyValue(HANDLE key,const wchar_t * value_name,ULONG * out_type,std::vector<BYTE> * out_buffer)798 bool QueryRegKeyValue(HANDLE key,
799 const wchar_t* value_name,
800 ULONG* out_type,
801 std::vector<BYTE>* out_buffer) {
802 if (!g_initialized && !InitNativeRegApi())
803 return false;
804
805 UNICODE_STRING value_uni = {};
806 g_rtl_init_unicode_string(&value_uni, value_name);
807
808 // Use a loop here, to be a little more tolerant of concurrent registry
809 // changes.
810 NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
811 int tries = 0;
812 KEY_VALUE_FULL_INFORMATION* value_info = nullptr;
813 DWORD size_needed = sizeof(*value_info);
814 std::vector<BYTE> buffer(size_needed);
815 do {
816 buffer.resize(size_needed);
817 value_info = reinterpret_cast<KEY_VALUE_FULL_INFORMATION*>(buffer.data());
818
819 ntstatus = g_nt_query_value_key(key, &value_uni, KeyValueFullInformation,
820 value_info, size_needed, &size_needed);
821 } while ((ntstatus == STATUS_BUFFER_OVERFLOW ||
822 ntstatus == STATUS_BUFFER_TOO_SMALL) &&
823 ++tries < kMaxTries);
824
825 if (!NT_SUCCESS(ntstatus))
826 return false;
827
828 *out_type = value_info->Type;
829 DWORD data_size = value_info->DataLength;
830
831 if (data_size) {
832 // Move the data into |out_buffer| vector.
833 BYTE* data = reinterpret_cast<BYTE*>(value_info) + value_info->DataOffset;
834 out_buffer->assign(data, data + data_size);
835 } else {
836 out_buffer->clear();
837 }
838
839 return true;
840 }
841
842 // wrapper function
QueryRegValueDWORD(HANDLE key,const wchar_t * value_name,DWORD * out_dword)843 bool QueryRegValueDWORD(HANDLE key,
844 const wchar_t* value_name,
845 DWORD* out_dword) {
846 ULONG type = REG_NONE;
847 std::vector<BYTE> value_bytes;
848
849 if (!QueryRegKeyValue(key, value_name, &type, &value_bytes) ||
850 type != REG_DWORD) {
851 return false;
852 }
853
854 if (value_bytes.size() < sizeof(*out_dword))
855 return false;
856
857 *out_dword = *(reinterpret_cast<DWORD*>(value_bytes.data()));
858
859 return true;
860 }
861
862 // wrapper function
QueryRegValueDWORD(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,DWORD * out_dword)863 bool QueryRegValueDWORD(ROOT_KEY root,
864 WOW64_OVERRIDE wow64_override,
865 const wchar_t* key_path,
866 const wchar_t* value_name,
867 DWORD* out_dword) {
868 HANDLE key = INVALID_HANDLE_VALUE;
869
870 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
871 return false;
872
873 if (!QueryRegValueDWORD(key, value_name, out_dword)) {
874 CloseRegKey(key);
875 return false;
876 }
877
878 CloseRegKey(key);
879 return true;
880 }
881
882 // wrapper function
QueryRegValueSZ(HANDLE key,const wchar_t * value_name,std::wstring * out_sz)883 bool QueryRegValueSZ(HANDLE key,
884 const wchar_t* value_name,
885 std::wstring* out_sz) {
886 std::vector<BYTE> value_bytes;
887 ULONG type = REG_NONE;
888
889 if (!QueryRegKeyValue(key, value_name, &type, &value_bytes) ||
890 (type != REG_SZ && type != REG_EXPAND_SZ)) {
891 return false;
892 }
893
894 EnsureTerminatedSZ(&value_bytes, false);
895
896 *out_sz = reinterpret_cast<wchar_t*>(value_bytes.data());
897
898 return true;
899 }
900
901 // wrapper function
QueryRegValueSZ(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,std::wstring * out_sz)902 bool QueryRegValueSZ(ROOT_KEY root,
903 WOW64_OVERRIDE wow64_override,
904 const wchar_t* key_path,
905 const wchar_t* value_name,
906 std::wstring* out_sz) {
907 HANDLE key = INVALID_HANDLE_VALUE;
908
909 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
910 return false;
911
912 if (!QueryRegValueSZ(key, value_name, out_sz)) {
913 CloseRegKey(key);
914 return false;
915 }
916
917 CloseRegKey(key);
918 return true;
919 }
920
921 // wrapper function
QueryRegValueMULTISZ(HANDLE key,const wchar_t * value_name,std::vector<std::wstring> * out_multi_sz)922 bool QueryRegValueMULTISZ(HANDLE key,
923 const wchar_t* value_name,
924 std::vector<std::wstring>* out_multi_sz) {
925 std::vector<BYTE> value_bytes;
926 ULONG type = REG_NONE;
927
928 if (!QueryRegKeyValue(key, value_name, &type, &value_bytes) ||
929 type != REG_MULTI_SZ) {
930 return false;
931 }
932
933 EnsureTerminatedSZ(&value_bytes, true);
934
935 // Make sure the out vector is empty to start.
936 out_multi_sz->clear();
937
938 wchar_t* pointer = reinterpret_cast<wchar_t*>(value_bytes.data());
939 std::wstring temp = pointer;
940 // Loop. Each string is separated by '\0'. Another '\0' at very end (so 2 in
941 // a row).
942 while (!temp.empty()) {
943 pointer += temp.length() + 1;
944 out_multi_sz->push_back(std::move(temp));
945 temp = pointer;
946 }
947
948 return true;
949 }
950
951 // wrapper function
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)952 bool QueryRegValueMULTISZ(ROOT_KEY root,
953 WOW64_OVERRIDE wow64_override,
954 const wchar_t* key_path,
955 const wchar_t* value_name,
956 std::vector<std::wstring>* out_multi_sz) {
957 HANDLE key = INVALID_HANDLE_VALUE;
958
959 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | wow64_override, &key, NULL))
960 return false;
961
962 if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) {
963 CloseRegKey(key);
964 return false;
965 }
966
967 CloseRegKey(key);
968 return true;
969 }
970
971 //------------------------------------------------------------------------------
972 // Setter functions
973 //------------------------------------------------------------------------------
974
SetRegKeyValue(HANDLE key,const wchar_t * value_name,ULONG type,const BYTE * data,DWORD data_size)975 bool SetRegKeyValue(HANDLE key,
976 const wchar_t* value_name,
977 ULONG type,
978 const BYTE* data,
979 DWORD data_size) {
980 if (!g_initialized && !InitNativeRegApi())
981 return false;
982
983 NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
984 UNICODE_STRING value_uni = {};
985 g_rtl_init_unicode_string(&value_uni, value_name);
986
987 BYTE* non_const_data = const_cast<BYTE*>(data);
988 ntstatus =
989 g_nt_set_value_key(key, &value_uni, 0, type, non_const_data, data_size);
990
991 if (NT_SUCCESS(ntstatus))
992 return true;
993
994 return false;
995 }
996
997 // wrapper function
SetRegValueDWORD(HANDLE key,const wchar_t * value_name,DWORD value)998 bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) {
999 return SetRegKeyValue(key, value_name, REG_DWORD,
1000 reinterpret_cast<BYTE*>(&value), sizeof(value));
1001 }
1002
1003 // wrapper function
SetRegValueDWORD(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,DWORD value)1004 bool SetRegValueDWORD(ROOT_KEY root,
1005 WOW64_OVERRIDE wow64_override,
1006 const wchar_t* key_path,
1007 const wchar_t* value_name,
1008 DWORD value) {
1009 HANDLE key = INVALID_HANDLE_VALUE;
1010
1011 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL))
1012 return false;
1013
1014 if (!SetRegValueDWORD(key, value_name, value)) {
1015 CloseRegKey(key);
1016 return false;
1017 }
1018
1019 return true;
1020 }
1021
1022 // wrapper function
SetRegValueSZ(HANDLE key,const wchar_t * value_name,const std::wstring & value)1023 bool SetRegValueSZ(HANDLE key,
1024 const wchar_t* value_name,
1025 const std::wstring& value) {
1026 // Make sure the number of bytes in |value|, including EoS, fits in a DWORD.
1027 if (std::numeric_limits<DWORD>::max() <
1028 ((value.length() + 1) * sizeof(wchar_t)))
1029 return false;
1030
1031 DWORD size = (static_cast<DWORD>((value.length() + 1) * sizeof(wchar_t)));
1032 return SetRegKeyValue(key, value_name, REG_SZ,
1033 reinterpret_cast<const BYTE*>(value.c_str()), size);
1034 }
1035
1036 // wrapper function
SetRegValueSZ(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,const std::wstring & value)1037 bool SetRegValueSZ(ROOT_KEY root,
1038 WOW64_OVERRIDE wow64_override,
1039 const wchar_t* key_path,
1040 const wchar_t* value_name,
1041 const std::wstring& value) {
1042 HANDLE key = INVALID_HANDLE_VALUE;
1043
1044 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL))
1045 return false;
1046
1047 if (!SetRegValueSZ(key, value_name, value)) {
1048 CloseRegKey(key);
1049 return false;
1050 }
1051
1052 return true;
1053 }
1054
1055 // wrapper function
SetRegValueMULTISZ(HANDLE key,const wchar_t * value_name,const std::vector<std::wstring> & values)1056 bool SetRegValueMULTISZ(HANDLE key,
1057 const wchar_t* value_name,
1058 const std::vector<std::wstring>& values) {
1059 std::vector<wchar_t> builder;
1060
1061 for (auto& string : values) {
1062 // Just in case someone is passing in an illegal empty string
1063 // (not allowed in REG_MULTI_SZ), ignore it.
1064 if (!string.empty()) {
1065 for (const wchar_t& w : string) {
1066 builder.push_back(w);
1067 }
1068 builder.push_back(L'\0');
1069 }
1070 }
1071 // Add second null terminator to end REG_MULTI_SZ.
1072 builder.push_back(L'\0');
1073 // Handle rare case where the vector passed in was empty,
1074 // or only had an empty string.
1075 if (builder.size() == 1)
1076 builder.push_back(L'\0');
1077
1078 if (std::numeric_limits<DWORD>::max() < builder.size())
1079 return false;
1080
1081 return SetRegKeyValue(
1082 key, value_name, REG_MULTI_SZ, reinterpret_cast<BYTE*>(builder.data()),
1083 (static_cast<DWORD>(builder.size()) + 1) * sizeof(wchar_t));
1084 }
1085
1086 // wrapper function
SetRegValueMULTISZ(ROOT_KEY root,WOW64_OVERRIDE wow64_override,const wchar_t * key_path,const wchar_t * value_name,const std::vector<std::wstring> & values)1087 bool SetRegValueMULTISZ(ROOT_KEY root,
1088 WOW64_OVERRIDE wow64_override,
1089 const wchar_t* key_path,
1090 const wchar_t* value_name,
1091 const std::vector<std::wstring>& values) {
1092 HANDLE key = INVALID_HANDLE_VALUE;
1093
1094 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | wow64_override, &key, NULL))
1095 return false;
1096
1097 if (!SetRegValueMULTISZ(key, value_name, values)) {
1098 CloseRegKey(key);
1099 return false;
1100 }
1101
1102 return true;
1103 }
1104
1105 //------------------------------------------------------------------------------
1106 // Enumeration Support
1107 //------------------------------------------------------------------------------
1108
QueryRegEnumerationInfo(HANDLE key,ULONG * out_subkey_count)1109 bool QueryRegEnumerationInfo(HANDLE key, ULONG* out_subkey_count) {
1110 if (!g_initialized && !InitNativeRegApi())
1111 return false;
1112
1113 // Use a loop here, to be a little more tolerant of concurrent registry
1114 // changes.
1115 NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
1116 int tries = 0;
1117 // Start with sizeof the structure. It's very common for the variable sized
1118 // "Class" element to be of length 0.
1119 KEY_FULL_INFORMATION* key_info = nullptr;
1120 DWORD size_needed = sizeof(*key_info);
1121 std::vector<BYTE> buffer(size_needed);
1122 do {
1123 buffer.resize(size_needed);
1124 key_info = reinterpret_cast<KEY_FULL_INFORMATION*>(buffer.data());
1125
1126 ntstatus = g_nt_query_key(key, KeyFullInformation, key_info, size_needed,
1127 &size_needed);
1128 } while ((ntstatus == STATUS_BUFFER_OVERFLOW ||
1129 ntstatus == STATUS_BUFFER_TOO_SMALL) &&
1130 ++tries < kMaxTries);
1131
1132 if (!NT_SUCCESS(ntstatus))
1133 return false;
1134
1135 // Move desired information to out variables.
1136 *out_subkey_count = key_info->SubKeys;
1137
1138 return true;
1139 }
1140
QueryRegSubkey(HANDLE key,ULONG subkey_index,std::wstring * out_subkey_name)1141 bool QueryRegSubkey(HANDLE key,
1142 ULONG subkey_index,
1143 std::wstring* out_subkey_name) {
1144 if (!g_initialized && !InitNativeRegApi())
1145 return false;
1146
1147 // Use a loop here, to be a little more tolerant of concurrent registry
1148 // changes.
1149 NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
1150 int tries = 0;
1151 // Start with sizeof the structure, plus 12 characters. It's very common for
1152 // key names to be < 12 characters (without being inefficient as an initial
1153 // allocation).
1154 KEY_BASIC_INFORMATION* subkey_info = nullptr;
1155 DWORD size_needed = sizeof(*subkey_info) + (12 * sizeof(wchar_t));
1156 std::vector<BYTE> buffer(size_needed);
1157 do {
1158 buffer.resize(size_needed);
1159 subkey_info = reinterpret_cast<KEY_BASIC_INFORMATION*>(buffer.data());
1160
1161 ntstatus = g_nt_enumerate_key(key, subkey_index, KeyBasicInformation,
1162 subkey_info, size_needed, &size_needed);
1163 } while ((ntstatus == STATUS_BUFFER_OVERFLOW ||
1164 ntstatus == STATUS_BUFFER_TOO_SMALL) &&
1165 ++tries < kMaxTries);
1166
1167 if (!NT_SUCCESS(ntstatus))
1168 return false;
1169
1170 // Move desired information to out variables.
1171 // NOTE: NameLength is size of Name array in bytes. Name array is also
1172 // NOT null terminated!
1173 BYTE* name = reinterpret_cast<BYTE*>(subkey_info->Name);
1174 std::vector<BYTE> content(name, name + subkey_info->NameLength);
1175 EnsureTerminatedSZ(&content, false);
1176 out_subkey_name->assign(reinterpret_cast<wchar_t*>(content.data()));
1177
1178 return true;
1179 }
1180
1181 //------------------------------------------------------------------------------
1182 // Utils
1183 //------------------------------------------------------------------------------
1184
GetCurrentUserSidString()1185 const wchar_t* GetCurrentUserSidString() {
1186 if (!g_initialized && !InitNativeRegApi())
1187 return nullptr;
1188
1189 return g_current_user_sid_string;
1190 }
1191
IsCurrentProcWow64()1192 bool IsCurrentProcWow64() {
1193 if (!g_initialized && !InitNativeRegApi())
1194 return false;
1195
1196 return g_wow64_proc;
1197 }
1198
SetTestingOverride(ROOT_KEY root,const std::wstring & new_path)1199 bool SetTestingOverride(ROOT_KEY root, const std::wstring& new_path) {
1200 if (!g_initialized && !InitNativeRegApi())
1201 return false;
1202
1203 std::wstring sani_new_path = new_path;
1204 SanitizeSubkeyPath(&sani_new_path);
1205 if (sani_new_path.length() > g_kRegMaxPathLen)
1206 return false;
1207
1208 if (root == HKCU || (root == AUTO && !g_system_install))
1209 ::wcsncpy(g_HKCU_override, sani_new_path.c_str(), nt::g_kRegMaxPathLen);
1210 else
1211 ::wcsncpy(g_HKLM_override, sani_new_path.c_str(), nt::g_kRegMaxPathLen);
1212
1213 return true;
1214 }
1215
GetTestingOverride(ROOT_KEY root)1216 std::wstring GetTestingOverride(ROOT_KEY root) {
1217 if (!g_initialized && !InitNativeRegApi())
1218 return std::wstring();
1219
1220 if (root == HKCU || (root == AUTO && !g_system_install))
1221 return g_HKCU_override;
1222
1223 return g_HKLM_override;
1224 }
1225
1226 } // namespace nt
1227