Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(560)

Side by Side Diff: chrome_elf/nt_registry/nt_registry.cc

Issue 1841573002: [Chrome ELF] New NT registry API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Nit fixes & CreateRegKey now recursive. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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_elf/nt_registry/nt_registry.h"
6
7 namespace {
8
9 // Function pointers used for registry access.
10 RtlInitUnicodeStringFunction g_rtl_init_unicode_string = nullptr;
11 NtCreateKeyFunction g_nt_create_key = nullptr;
12 NtDeleteKeyFunction g_nt_delete_key = nullptr;
13 NtOpenKeyExFunction g_nt_open_key_ex = nullptr;
14 NtCloseFunction g_nt_close = nullptr;
15 NtQueryValueKeyFunction g_nt_query_value_key = nullptr;
16 NtSetValueKeyFunction g_nt_set_value_key = nullptr;
17
18 // Lazy init. No concern about concurrency in chrome_elf.
19 bool g_initialized = false;
20 bool g_system_install = false;
21 const size_t g_kRegMaxPathLen = 255;
22 wchar_t g_kRegPathHKLM[] = L"\\Registry\\Machine\\";
23 wchar_t g_kRegPathHKCU[g_kRegMaxPathLen] = L"";
24 std::wstring g_current_user_sid_string;
25 std::wstring g_override_path;
26
27 // Not using install_util, to prevent circular dependency.
28 bool IsThisProcSystem() {
29 wchar_t program_dir[MAX_PATH] = {};
30 wchar_t* cmd_line = GetCommandLineW();
31 // If our command line starts with the "Program Files" or
32 // "Program Files (x86)" path, we're system.
33 DWORD ret = ::GetEnvironmentVariable(L"PROGRAMFILES", program_dir, MAX_PATH);
34 if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
35 return true;
36
37 ret = ::GetEnvironmentVariable(L"PROGRAMFILES(X86)", program_dir, MAX_PATH);
38 if (ret && ret < MAX_PATH && !::wcsncmp(cmd_line, program_dir, ret))
39 return true;
40
41 return false;
42 }
43
44 bool InitNativeRegApi() {
45 HMODULE ntdll = ::GetModuleHandleW(L"ntdll.dll");
46
47 // Setup the global function pointers for registry access.
48 g_rtl_init_unicode_string = reinterpret_cast<RtlInitUnicodeStringFunction>(
49 ::GetProcAddress(ntdll, "RtlInitUnicodeString"));
50
51 g_nt_create_key = reinterpret_cast<NtCreateKeyFunction>(
52 ::GetProcAddress(ntdll, "NtCreateKey"));
53
54 g_nt_delete_key = reinterpret_cast<NtDeleteKeyFunction>(
55 ::GetProcAddress(ntdll, "NtDeleteKey"));
56
57 g_nt_open_key_ex = reinterpret_cast<NtOpenKeyExFunction>(
58 ::GetProcAddress(ntdll, "NtOpenKeyEx"));
59
60 g_nt_close =
61 reinterpret_cast<NtCloseFunction>(::GetProcAddress(ntdll, "NtClose"));
62
63 g_nt_query_value_key = reinterpret_cast<NtQueryValueKeyFunction>(
64 ::GetProcAddress(ntdll, "NtQueryValueKey"));
65
66 g_nt_set_value_key = reinterpret_cast<NtSetValueKeyFunction>(
67 ::GetProcAddress(ntdll, "NtSetValueKey"));
68
69 if (!g_rtl_init_unicode_string || !g_nt_create_key || !g_nt_open_key_ex ||
70 !g_nt_delete_key || !g_nt_close || !g_nt_query_value_key ||
71 !g_nt_set_value_key)
72 return false;
73
74 // We need to set HKCU based on the sid of the current user account.
75 RtlFormatCurrentUserKeyPathFunction rtl_current_user_string =
76 reinterpret_cast<RtlFormatCurrentUserKeyPathFunction>(
77 ::GetProcAddress(ntdll, "RtlFormatCurrentUserKeyPath"));
78
79 RtlFreeUnicodeStringFunction rtl_free_unicode_str =
80 reinterpret_cast<RtlFreeUnicodeStringFunction>(
81 ::GetProcAddress(ntdll, "RtlFreeUnicodeString"));
82
83 if (!rtl_current_user_string || !rtl_free_unicode_str)
84 return false;
85
86 UNICODE_STRING current_user_reg_path;
87 if (!NT_SUCCESS(rtl_current_user_string(&current_user_reg_path)))
88 return false;
89
90 // Finish setting up global HKCU path.
91 ::wcsncat(g_kRegPathHKCU, current_user_reg_path.Buffer, g_kRegMaxPathLen - 1);
92 ::wcsncat(g_kRegPathHKCU, L"\\",
93 (g_kRegMaxPathLen - ::wcslen(g_kRegPathHKCU) - 1));
94 // Keep the sid string as well.
95 wchar_t* ptr = ::wcsrchr(current_user_reg_path.Buffer, L'\\');
96 ptr++;
97 g_current_user_sid_string.assign(ptr);
98 rtl_free_unicode_str(&current_user_reg_path);
99
100 // Figure out if we're a system or user install.
101 g_system_install = IsThisProcSystem();
102
103 g_initialized = true;
104 return true;
105 }
106
107 const wchar_t* ConvertRootKey(nt::ROOT_KEY root) {
108 nt::ROOT_KEY key = root;
109
110 if (!root) {
111 // AUTO
112 key = g_system_install ? nt::HKLM : nt::HKCU;
113 }
114
115 if ((key == nt::HKCU) && (!nt::HKCU_override.empty())) {
116 g_override_path.assign(g_kRegPathHKCU);
117 g_override_path.append(nt::HKCU_override.c_str());
118 g_override_path.append(L"\\");
119 return g_override_path.c_str();
120 } else if ((key == nt::HKLM) && (!nt::HKLM_override.empty())) {
121 g_override_path.assign(g_kRegPathHKCU);
122 g_override_path.append(nt::HKLM_override.c_str());
123 g_override_path.append(L"\\");
124 return g_override_path.c_str();
125 }
126
127 if (key == nt::HKCU)
128 return g_kRegPathHKCU;
129 else
130 return g_kRegPathHKLM;
131 }
132
133 NTSTATUS CreateKeyWrapper(const std::wstring& key_path,
134 ACCESS_MASK access,
135 HANDLE* out_handle,
136 ULONG* create_or_open OPTIONAL) {
robertshield 2016/06/23 02:29:36 I'm haven't seen the OPTIONAL annotation used befo
penny 2016/06/25 21:13:43 So, I started a little discussion with my fellow c
137 UNICODE_STRING key_path_uni = {};
138 g_rtl_init_unicode_string(&key_path_uni, key_path.c_str());
139
140 OBJECT_ATTRIBUTES obj = {};
141 InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
142 nullptr);
143
144 return g_nt_create_key(out_handle, access, &obj, 0, nullptr,
145 REG_OPTION_NON_VOLATILE, create_or_open);
146 }
147
148 // |root_path| should already exist.
149 bool CreateRegKeyRecursive(const std::wstring& root_path,
150 const std::wstring& sub_key_path,
151 ACCESS_MASK access,
152 HANDLE* out_handle) {
robertshield 2016/06/23 02:29:36 fwiw, you can do this more simply (and more effici
penny 2016/06/25 21:13:43 Thanks Robert. I was thinking it would be easier
153 std::wstring new_sub_key_path;
154 std::wstring new_root_path(root_path);
155 std::wstring next_key(sub_key_path);
156
157 // See if this is the last sub-key.
158 size_t index = sub_key_path.find_first_of(L"\\", 0);
robertshield 2016/06/23 02:29:36 you're searching for one character, so you can use
penny 2016/06/25 21:13:43 Acknowledged.
159 if (index != std::wstring::npos) {
160 // Save the remaining key path.
161 new_sub_key_path = sub_key_path.substr(index + 1, std::wstring::npos);
162 // Adjust next_key to just be the first sub key.
163 next_key.resize(index);
164 }
165 new_root_path.push_back(L'\\');
166 new_root_path.append(next_key);
167
168 // Create the next key. It might already exist.
169 HANDLE temp_handle = INVALID_HANDLE_VALUE;
170 ULONG create_or_open = 0;
171 NTSTATUS status =
172 CreateKeyWrapper(new_root_path, access, &temp_handle, &create_or_open);
173 if (!NT_SUCCESS(status)) {
174 *out_handle = INVALID_HANDLE_VALUE;
175 return false;
176 }
177
178 // See if done.
179 if (new_sub_key_path.empty()) {
180 // Don't close temp_handle in this case. Pass it out in |out_handle|.
181 *out_handle = temp_handle;
182 return true;
183 }
184
185 // Recursively call this function on |new_sub_key_path|.
186 if (!CreateRegKeyRecursive(new_root_path, new_sub_key_path, access,
187 out_handle)) {
188 // On failure, "clean up" newly created subkeys only. If it already
189 // existed, leave it.
190 if (create_or_open == REG_CREATED_NEW_KEY)
191 g_nt_delete_key(temp_handle);
192 g_nt_close(temp_handle);
193 return false;
194 }
195
196 g_nt_close(temp_handle);
197 return true;
198 }
199
200 } // namespace
201
202 namespace nt {
203
204 std::wstring HKLM_override;
205 std::wstring HKCU_override;
206
207 //------------------------------------------------------------------------------
208 // Create, open, delete, close functions
209 //------------------------------------------------------------------------------
210
211 bool CreateRegKey(ROOT_KEY root,
212 const wchar_t* key_path,
213 ACCESS_MASK access,
214 HANDLE* out_handle OPTIONAL) {
215 if (!g_initialized)
216 InitNativeRegApi();
217
218 // Open the root first. Using create instead of open, because
219 // there is small chance a full redirection root does not exist yet.
220 std::wstring root_path(ConvertRootKey(root));
221 HANDLE root_handle = INVALID_HANDLE_VALUE;
222 NTSTATUS status = CreateKeyWrapper(root_path, access, &root_handle, nullptr);
223 if (!NT_SUCCESS(status))
224 return false;
225
226 // Make sure |key_path| does not start or end with a path seperator.
227 std::wstring sub_path(key_path);
228 if (sub_path.front() == L'\\')
229 sub_path.erase(0, 1);
230 if (sub_path.back() == L'\\')
231 sub_path.pop_back();
232
233 // Recursively create the rest of the sub keys.
234 HANDLE key_handle = INVALID_HANDLE_VALUE;
235 bool success =
236 CreateRegKeyRecursive(root_path, sub_path, access, &key_handle);
237 CloseRegKey(root_handle);
238
239 if (success) {
240 if (out_handle)
241 *out_handle = key_handle;
242 else
243 CloseRegKey(key_handle);
244 return true;
245 }
246
247 return false;
248 }
249
250 bool OpenRegKey(ROOT_KEY root,
251 const wchar_t* key_path,
252 ACCESS_MASK access,
253 HANDLE* out_handle,
254 NTSTATUS* error_code OPTIONAL) {
255 if (!g_initialized)
256 InitNativeRegApi();
257
258 NTSTATUS status = STATUS_UNSUCCESSFUL;
259 UNICODE_STRING key_path_uni = {};
260 OBJECT_ATTRIBUTES obj = {};
261 *out_handle = INVALID_HANDLE_VALUE;
262
263 std::wstring full_path(ConvertRootKey(root));
264 full_path.append(key_path);
265
266 g_rtl_init_unicode_string(&key_path_uni, full_path.c_str());
267 InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
268 NULL);
269
270 status = g_nt_open_key_ex(out_handle, access, &obj, 0);
271 // See if caller wants the NTSTATUS.
272 if (error_code)
273 *error_code = status;
274
275 if (NT_SUCCESS(status))
276 return true;
277
278 return false;
279 }
280
281 bool DeleteRegKey(HANDLE key) {
282 if (!g_initialized)
283 InitNativeRegApi();
284
285 NTSTATUS status = STATUS_UNSUCCESSFUL;
286
287 status = g_nt_delete_key(key);
288
289 if (NT_SUCCESS(status))
290 return true;
291
292 return false;
293 }
294
295 // wrapper function
296 bool DeleteRegKey(ROOT_KEY root, const wchar_t* key_path) {
297 HANDLE key = INVALID_HANDLE_VALUE;
298
299 if (!OpenRegKey(root, key_path, DELETE, &key, nullptr))
300 return false;
301
302 if (!DeleteRegKey(key)) {
303 CloseRegKey(key);
304 return false;
305 }
306
307 CloseRegKey(key);
308 return true;
309 }
310
311 void CloseRegKey(HANDLE key) {
312 if (!g_initialized)
313 InitNativeRegApi();
314 g_nt_close(key);
315 }
316
317 //------------------------------------------------------------------------------
318 // Getter functions
319 //------------------------------------------------------------------------------
320
321 bool QueryRegKeyValue(HANDLE key,
322 const wchar_t* value_name,
323 ULONG* out_type,
324 BYTE** out_buffer,
325 DWORD* out_size) {
326 if (!g_initialized)
327 InitNativeRegApi();
328
329 NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
330 UNICODE_STRING value_uni = {};
331 g_rtl_init_unicode_string(&value_uni, value_name);
332 DWORD size_needed = 0;
333 bool success = false;
334
335 // First call to find out how much room we need for the value!
336 ntstatus = g_nt_query_value_key(key, &value_uni, KeyValueFullInformation,
337 nullptr, 0, &size_needed);
338 if (ntstatus != STATUS_BUFFER_TOO_SMALL)
339 return false;
340
341 KEY_VALUE_FULL_INFORMATION* value_info =
342 reinterpret_cast<KEY_VALUE_FULL_INFORMATION*>(new BYTE[size_needed]);
343
344 // Second call to get the value.
345 ntstatus = g_nt_query_value_key(key, &value_uni, KeyValueFullInformation,
346 value_info, size_needed, &size_needed);
347 if (NT_SUCCESS(ntstatus)) {
348 *out_type = value_info->Type;
349 *out_size = value_info->DataLength;
350 *out_buffer = new BYTE[*out_size];
351 ::memcpy(*out_buffer,
352 (reinterpret_cast<BYTE*>(value_info) + value_info->DataOffset),
353 *out_size);
354 success = true;
355 }
356
357 delete[] value_info;
358 return success;
359 }
360
361 // wrapper function
362 bool QueryRegValueDWORD(HANDLE key,
363 const wchar_t* value_name,
364 DWORD* out_dword) {
365 ULONG type = REG_NONE;
366 BYTE* value_bytes = nullptr;
367 DWORD ret_size = 0;
368
369 if (!QueryRegKeyValue(key, value_name, &type, &value_bytes, &ret_size) ||
370 type != REG_DWORD)
371 return false;
372
373 *out_dword = *(reinterpret_cast<DWORD*>(value_bytes));
374
375 delete[] value_bytes;
376 return true;
377 }
378
379 // wrapper function
380 bool QueryRegValueDWORD(ROOT_KEY root,
381 const wchar_t* key_path,
382 const wchar_t* value_name,
383 DWORD* out_dword) {
384 HANDLE key = INVALID_HANDLE_VALUE;
385
386 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key,
387 NULL))
388 return false;
389
390 if (!QueryRegValueDWORD(key, value_name, out_dword)) {
391 CloseRegKey(key);
392 return false;
393 }
394
395 CloseRegKey(key);
396 return true;
397 }
398
399 // wrapper function
400 bool QueryRegValueSZ(HANDLE key,
401 const wchar_t* value_name,
402 std::wstring* out_sz) {
403 BYTE* value_bytes = nullptr;
404 DWORD ret_size = 0;
405 ULONG type = REG_NONE;
406
407 if (!QueryRegKeyValue(key, value_name, &type, &value_bytes, &ret_size) ||
408 type != REG_SZ)
409 return false;
410
411 *out_sz = reinterpret_cast<wchar_t*>(value_bytes);
412
413 delete[] value_bytes;
414 return true;
415 }
416
417 // wrapper function
418 bool QueryRegValueSZ(ROOT_KEY root,
419 const wchar_t* key_path,
420 const wchar_t* value_name,
421 std::wstring* out_sz) {
422 HANDLE key = INVALID_HANDLE_VALUE;
423
424 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key,
425 NULL))
426 return false;
427
428 if (!QueryRegValueSZ(key, value_name, out_sz)) {
429 CloseRegKey(key);
430 return false;
431 }
432
433 CloseRegKey(key);
434 return true;
435 }
436
437 // wrapper function
438 bool QueryRegValueMULTISZ(HANDLE key,
439 const wchar_t* value_name,
440 std::vector<std::wstring>* out_multi_sz) {
441 BYTE* value_bytes = nullptr;
442 DWORD ret_size = 0;
443 ULONG type = REG_NONE;
444
445 if (!QueryRegKeyValue(key, value_name, &type, &value_bytes, &ret_size) ||
446 type != REG_MULTI_SZ)
447 return false;
448
449 // Make sure the vector is empty to start.
450 (*out_multi_sz).resize(0);
451
452 wchar_t* pointer = reinterpret_cast<wchar_t*>(value_bytes);
453 std::wstring temp = pointer;
454 // Loop. Each string is separated by '\0'. Another '\0' at very end (so 2 in
455 // a row).
456 while (temp.length() != 0) {
457 (*out_multi_sz).push_back(temp);
458
459 pointer += temp.length() + 1;
460 temp = pointer;
461 }
462
463 // Handle the case of "empty multi_sz".
464 if (out_multi_sz->size() == 0)
465 out_multi_sz->push_back(L"");
466
467 delete[] value_bytes;
468 return true;
469 }
470
471 // wrapper function
472 bool QueryRegValueMULTISZ(ROOT_KEY root,
473 const wchar_t* key_path,
474 const wchar_t* value_name,
475 std::vector<std::wstring>* out_multi_sz) {
476 HANDLE key = INVALID_HANDLE_VALUE;
477
478 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key,
479 NULL))
480 return false;
481
482 if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) {
483 CloseRegKey(key);
484 return false;
485 }
486
487 CloseRegKey(key);
488 return true;
489 }
490
491 //------------------------------------------------------------------------------
492 // Setter functions
493 //------------------------------------------------------------------------------
494
495 bool SetRegKeyValue(HANDLE key,
496 const wchar_t* value_name,
497 ULONG type,
498 const BYTE* data,
499 DWORD data_size) {
500 if (!g_initialized)
501 InitNativeRegApi();
502
503 NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
504 UNICODE_STRING value_uni = {};
505 g_rtl_init_unicode_string(&value_uni, value_name);
506
507 BYTE* non_const_data = const_cast<BYTE*>(data);
508 ntstatus =
509 g_nt_set_value_key(key, &value_uni, 0, type, non_const_data, data_size);
510
511 if (NT_SUCCESS(ntstatus))
512 return true;
513
514 return false;
515 }
516
517 // wrapper function
518 bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) {
519 return SetRegKeyValue(key, value_name, REG_DWORD,
520 reinterpret_cast<BYTE*>(&value), sizeof(value));
521 }
522
523 // wrapper function
524 bool SetRegValueDWORD(ROOT_KEY root,
525 const wchar_t* key_path,
526 const wchar_t* value_name,
527 DWORD value) {
528 HANDLE key = INVALID_HANDLE_VALUE;
529
530 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL))
531 return false;
532
533 if (!SetRegValueDWORD(key, value_name, value)) {
534 CloseRegKey(key);
535 return false;
536 }
537
538 return true;
539 }
540
541 // wrapper function
542 bool SetRegValueSZ(HANDLE key,
543 const wchar_t* value_name,
544 const std::wstring& value) {
545 // Make sure the number of bytes in |value|, including EoS, fits in a DWORD.
546 if (std::numeric_limits<DWORD>::max() <
547 ((value.length() + 1) * sizeof(wchar_t)))
548 return false;
549
550 DWORD size = (static_cast<DWORD>((value.length() + 1) * sizeof(wchar_t)));
551 return SetRegKeyValue(key, value_name, REG_SZ,
552 reinterpret_cast<const BYTE*>(value.c_str()), size);
553 }
554
555 // wrapper function
556 bool SetRegValueSZ(ROOT_KEY root,
557 const wchar_t* key_path,
558 const wchar_t* value_name,
559 const std::wstring& value) {
560 HANDLE key = INVALID_HANDLE_VALUE;
561
562 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL))
563 return false;
564
565 if (!SetRegValueSZ(key, value_name, value)) {
566 CloseRegKey(key);
567 return false;
568 }
569
570 return true;
571 }
572
573 // wrapper function
574 bool SetRegValueMULTISZ(HANDLE key,
575 const wchar_t* value_name,
576 const std::vector<std::wstring>& values) {
577 std::vector<wchar_t> builder;
578
579 for (auto& string : values) {
580 // Just in case someone is passing in an illegal empty string
581 // (not allowed in REG_MULTI_SZ), ignore it.
582 if (!string.empty()) {
583 for (const wchar_t& w : string) {
584 builder.push_back(w);
585 }
586 builder.push_back(L'\0');
587 }
588 }
589 // Add second null terminator to end REG_MULTI_SZ.
590 builder.push_back(L'\0');
591 // Handle rare case where the vector passed in was empty,
592 // or only had an empty string.
593 if (builder.size() == 1)
594 builder.push_back(L'\0');
595
596 if (std::numeric_limits<DWORD>::max() < builder.size())
597 return false;
598
599 return SetRegKeyValue(
600 key, value_name, REG_MULTI_SZ, reinterpret_cast<BYTE*>(builder.data()),
601 (static_cast<DWORD>(builder.size()) + 1) * sizeof(wchar_t));
602 }
603
604 // wrapper function
605 bool SetRegValueMULTISZ(ROOT_KEY root,
606 const wchar_t* key_path,
607 const wchar_t* value_name,
608 const std::vector<std::wstring>& values) {
609 HANDLE key = INVALID_HANDLE_VALUE;
610
611 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL))
612 return false;
613
614 if (!SetRegValueMULTISZ(key, value_name, values)) {
615 CloseRegKey(key);
616 return false;
617 }
618
619 return true;
620 }
621
622 //------------------------------------------------------------------------------
623 // Utils
624 //------------------------------------------------------------------------------
625
626 std::wstring GetCurrentUserSidString() {
627 if (!g_initialized)
628 InitNativeRegApi();
629
630 return g_current_user_sid_string;
631 }
632
633 }; // namespace nt
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698