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

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: Change recursion. Created 4 years, 5 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
« no previous file with comments | « chrome_elf/nt_registry/nt_registry.h ('k') | chrome_elf/nt_registry/nt_registry.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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;
robertshield 2016/06/26 02:11:04 I believe that static non-POD types are disallowed
penny 2016/07/11 19:59:04 Thanks for that. I was unaware of this restrictio
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) {
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 } // namespace
149
150 namespace nt {
151
152 std::wstring HKLM_override;
penny 2016/07/11 19:59:04 Robert, I adjusted these to be POD arrays as well.
153 std::wstring HKCU_override;
154
155 //------------------------------------------------------------------------------
156 // Create, open, delete, close functions
157 //------------------------------------------------------------------------------
158
159 bool CreateRegKey(ROOT_KEY root,
160 const wchar_t* key_path,
161 ACCESS_MASK access,
162 HANDLE* out_handle OPTIONAL) {
163 if (!g_initialized)
164 InitNativeRegApi();
165
166 // Open the root first. Using create instead of open, because
167 // there is small chance a full redirection root does not exist yet.
168 std::wstring root_path(ConvertRootKey(root));
169 HANDLE root_handle = INVALID_HANDLE_VALUE;
170 NTSTATUS status = CreateKeyWrapper(root_path, access, &root_handle, nullptr);
171 if (!NT_SUCCESS(status))
172 return false;
173
174 // Make sure |key_path| does NOT start with a path seperator.
175 std::wstring sub_path(key_path);
176 if (sub_path.front() == L'\\')
177 sub_path.erase(0, 1);
178 // Make sure |key_path| DOES end with a path seperator.
179 if (sub_path.back() != L'\\')
180 sub_path.push_back(L'\\');
181
182 std::wstring current_path(root_path);
183 std::vector<HANDLE> rollback;
184 bool success = true;
185
186 // Recursively create the rest of the sub keys.
187 for (std::wstring::iterator it = sub_path.begin(); it != sub_path.end();
188 ++it) {
189 if (*it == L'\\') {
190 // Process the latest subkey.
191 ULONG created = 0;
192 HANDLE key_handle = INVALID_HANDLE_VALUE;
193 status =
194 CreateKeyWrapper(current_path.c_str(), access, &key_handle, &created);
195 if (!NT_SUCCESS(status)) {
196 success = false;
197 break;
198 }
199 // Save any handle of subkey created, in case of rollback.
robertshield 2016/06/26 02:11:04 micro-nit: s/handle of subkey/subkey handle/
penny 2016/07/11 19:59:04 Done.
200 if (created == REG_CREATED_NEW_KEY)
201 rollback.push_back(&key_handle);
202 else
203 g_nt_close(key_handle);
204 current_path.push_back(L'\\');
205 } else {
206 current_path.push_back(*it);
207 }
208 }
209
210 if (!success) {
211 // Delete any subkeys created.
212 for (auto handle : rollback) {
213 g_nt_delete_key(handle);
robertshield 2016/06/26 02:11:03 This doesn't delete |root_handle| in case of failu
penny 2016/07/11 19:59:04 I've adjusted this a bit. Now I only open the bas
214 }
215 }
216
217 for (auto handle : rollback) {
218 g_nt_close(handle);
219 }
220 g_nt_close(root_handle);
robertshield 2016/06/26 02:11:04 If we do want to delete |root_handle| above in cas
penny 2016/07/11 19:59:04 Acknowledged.
221
222 if (!success)
223 return false;
224
225 // See if caller wants the handle open.
226 if (out_handle) {
robertshield 2016/06/26 02:11:04 slight optimization: could you check |out_handle|
penny 2016/07/11 19:59:04 Unfortunately, not every handle is on the vector -
227 // Reopen the final handle.
228 status =
229 CreateKeyWrapper(current_path.c_str(), access, out_handle, nullptr);
230 if (!NT_SUCCESS(status))
231 return false;
232 }
233
234 return true;
235 }
236
237 bool OpenRegKey(ROOT_KEY root,
238 const wchar_t* key_path,
239 ACCESS_MASK access,
240 HANDLE* out_handle,
241 NTSTATUS* error_code OPTIONAL) {
242 if (!g_initialized)
243 InitNativeRegApi();
244
245 NTSTATUS status = STATUS_UNSUCCESSFUL;
246 UNICODE_STRING key_path_uni = {};
247 OBJECT_ATTRIBUTES obj = {};
248 *out_handle = INVALID_HANDLE_VALUE;
249
250 std::wstring full_path(ConvertRootKey(root));
251 full_path.append(key_path);
252
253 g_rtl_init_unicode_string(&key_path_uni, full_path.c_str());
254 InitializeObjectAttributes(&obj, &key_path_uni, OBJ_CASE_INSENSITIVE, NULL,
255 NULL);
256
257 status = g_nt_open_key_ex(out_handle, access, &obj, 0);
258 // See if caller wants the NTSTATUS.
259 if (error_code)
260 *error_code = status;
261
262 if (NT_SUCCESS(status))
263 return true;
264
265 return false;
266 }
267
268 bool DeleteRegKey(HANDLE key) {
269 if (!g_initialized)
270 InitNativeRegApi();
271
272 NTSTATUS status = STATUS_UNSUCCESSFUL;
273
274 status = g_nt_delete_key(key);
275
276 if (NT_SUCCESS(status))
277 return true;
278
279 return false;
280 }
281
282 // wrapper function
283 bool DeleteRegKey(ROOT_KEY root, const wchar_t* key_path) {
284 HANDLE key = INVALID_HANDLE_VALUE;
285
286 if (!OpenRegKey(root, key_path, DELETE, &key, nullptr))
287 return false;
288
289 if (!DeleteRegKey(key)) {
290 CloseRegKey(key);
291 return false;
292 }
293
294 CloseRegKey(key);
295 return true;
296 }
297
298 void CloseRegKey(HANDLE key) {
299 if (!g_initialized)
300 InitNativeRegApi();
301 g_nt_close(key);
302 }
303
304 //------------------------------------------------------------------------------
305 // Getter functions
306 //------------------------------------------------------------------------------
307
308 bool QueryRegKeyValue(HANDLE key,
309 const wchar_t* value_name,
310 ULONG* out_type,
311 BYTE** out_buffer,
312 DWORD* out_size) {
313 if (!g_initialized)
314 InitNativeRegApi();
315
316 NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
317 UNICODE_STRING value_uni = {};
318 g_rtl_init_unicode_string(&value_uni, value_name);
319 DWORD size_needed = 0;
320 bool success = false;
321
322 // First call to find out how much room we need for the value!
323 ntstatus = g_nt_query_value_key(key, &value_uni, KeyValueFullInformation,
324 nullptr, 0, &size_needed);
325 if (ntstatus != STATUS_BUFFER_TOO_SMALL)
326 return false;
327
328 KEY_VALUE_FULL_INFORMATION* value_info =
329 reinterpret_cast<KEY_VALUE_FULL_INFORMATION*>(new BYTE[size_needed]);
330
331 // Second call to get the value.
332 ntstatus = g_nt_query_value_key(key, &value_uni, KeyValueFullInformation,
333 value_info, size_needed, &size_needed);
334 if (NT_SUCCESS(ntstatus)) {
335 *out_type = value_info->Type;
336 *out_size = value_info->DataLength;
337 *out_buffer = new BYTE[*out_size];
338 ::memcpy(*out_buffer,
339 (reinterpret_cast<BYTE*>(value_info) + value_info->DataOffset),
340 *out_size);
341 success = true;
342 }
343
344 delete[] value_info;
345 return success;
346 }
347
348 // wrapper function
349 bool QueryRegValueDWORD(HANDLE key,
350 const wchar_t* value_name,
351 DWORD* out_dword) {
352 ULONG type = REG_NONE;
353 BYTE* value_bytes = nullptr;
354 DWORD ret_size = 0;
355
356 if (!QueryRegKeyValue(key, value_name, &type, &value_bytes, &ret_size) ||
357 type != REG_DWORD)
358 return false;
359
360 *out_dword = *(reinterpret_cast<DWORD*>(value_bytes));
361
362 delete[] value_bytes;
363 return true;
364 }
365
366 // wrapper function
367 bool QueryRegValueDWORD(ROOT_KEY root,
368 const wchar_t* key_path,
369 const wchar_t* value_name,
370 DWORD* out_dword) {
371 HANDLE key = INVALID_HANDLE_VALUE;
372
373 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key,
374 NULL))
375 return false;
376
377 if (!QueryRegValueDWORD(key, value_name, out_dword)) {
378 CloseRegKey(key);
379 return false;
380 }
381
382 CloseRegKey(key);
383 return true;
384 }
385
386 // wrapper function
387 bool QueryRegValueSZ(HANDLE key,
388 const wchar_t* value_name,
389 std::wstring* out_sz) {
390 BYTE* value_bytes = nullptr;
391 DWORD ret_size = 0;
392 ULONG type = REG_NONE;
393
394 if (!QueryRegKeyValue(key, value_name, &type, &value_bytes, &ret_size) ||
395 type != REG_SZ)
396 return false;
397
398 *out_sz = reinterpret_cast<wchar_t*>(value_bytes);
399
400 delete[] value_bytes;
401 return true;
402 }
403
404 // wrapper function
405 bool QueryRegValueSZ(ROOT_KEY root,
406 const wchar_t* key_path,
407 const wchar_t* value_name,
408 std::wstring* out_sz) {
409 HANDLE key = INVALID_HANDLE_VALUE;
410
411 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key,
412 NULL))
413 return false;
414
415 if (!QueryRegValueSZ(key, value_name, out_sz)) {
416 CloseRegKey(key);
417 return false;
418 }
419
420 CloseRegKey(key);
421 return true;
422 }
423
424 // wrapper function
425 bool QueryRegValueMULTISZ(HANDLE key,
426 const wchar_t* value_name,
427 std::vector<std::wstring>* out_multi_sz) {
428 BYTE* value_bytes = nullptr;
429 DWORD ret_size = 0;
430 ULONG type = REG_NONE;
431
432 if (!QueryRegKeyValue(key, value_name, &type, &value_bytes, &ret_size) ||
433 type != REG_MULTI_SZ)
434 return false;
435
436 // Make sure the vector is empty to start.
437 (*out_multi_sz).resize(0);
438
439 wchar_t* pointer = reinterpret_cast<wchar_t*>(value_bytes);
440 std::wstring temp = pointer;
441 // Loop. Each string is separated by '\0'. Another '\0' at very end (so 2 in
442 // a row).
443 while (temp.length() != 0) {
444 (*out_multi_sz).push_back(temp);
445
446 pointer += temp.length() + 1;
447 temp = pointer;
448 }
449
450 // Handle the case of "empty multi_sz".
451 if (out_multi_sz->size() == 0)
452 out_multi_sz->push_back(L"");
453
454 delete[] value_bytes;
455 return true;
456 }
457
458 // wrapper function
459 bool QueryRegValueMULTISZ(ROOT_KEY root,
460 const wchar_t* key_path,
461 const wchar_t* value_name,
462 std::vector<std::wstring>* out_multi_sz) {
463 HANDLE key = INVALID_HANDLE_VALUE;
464
465 if (!OpenRegKey(root, key_path, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key,
466 NULL))
467 return false;
468
469 if (!QueryRegValueMULTISZ(key, value_name, out_multi_sz)) {
470 CloseRegKey(key);
471 return false;
472 }
473
474 CloseRegKey(key);
475 return true;
476 }
477
478 //------------------------------------------------------------------------------
479 // Setter functions
480 //------------------------------------------------------------------------------
481
482 bool SetRegKeyValue(HANDLE key,
483 const wchar_t* value_name,
484 ULONG type,
485 const BYTE* data,
486 DWORD data_size) {
487 if (!g_initialized)
488 InitNativeRegApi();
489
490 NTSTATUS ntstatus = STATUS_UNSUCCESSFUL;
491 UNICODE_STRING value_uni = {};
492 g_rtl_init_unicode_string(&value_uni, value_name);
493
494 BYTE* non_const_data = const_cast<BYTE*>(data);
495 ntstatus =
496 g_nt_set_value_key(key, &value_uni, 0, type, non_const_data, data_size);
497
498 if (NT_SUCCESS(ntstatus))
499 return true;
500
501 return false;
502 }
503
504 // wrapper function
505 bool SetRegValueDWORD(HANDLE key, const wchar_t* value_name, DWORD value) {
506 return SetRegKeyValue(key, value_name, REG_DWORD,
507 reinterpret_cast<BYTE*>(&value), sizeof(value));
508 }
509
510 // wrapper function
511 bool SetRegValueDWORD(ROOT_KEY root,
512 const wchar_t* key_path,
513 const wchar_t* value_name,
514 DWORD value) {
515 HANDLE key = INVALID_HANDLE_VALUE;
516
517 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL))
518 return false;
519
520 if (!SetRegValueDWORD(key, value_name, value)) {
521 CloseRegKey(key);
522 return false;
523 }
524
525 return true;
526 }
527
528 // wrapper function
529 bool SetRegValueSZ(HANDLE key,
530 const wchar_t* value_name,
531 const std::wstring& value) {
532 // Make sure the number of bytes in |value|, including EoS, fits in a DWORD.
533 if (std::numeric_limits<DWORD>::max() <
534 ((value.length() + 1) * sizeof(wchar_t)))
535 return false;
536
537 DWORD size = (static_cast<DWORD>((value.length() + 1) * sizeof(wchar_t)));
538 return SetRegKeyValue(key, value_name, REG_SZ,
539 reinterpret_cast<const BYTE*>(value.c_str()), size);
540 }
541
542 // wrapper function
543 bool SetRegValueSZ(ROOT_KEY root,
544 const wchar_t* key_path,
545 const wchar_t* value_name,
546 const std::wstring& value) {
547 HANDLE key = INVALID_HANDLE_VALUE;
548
549 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL))
550 return false;
551
552 if (!SetRegValueSZ(key, value_name, value)) {
553 CloseRegKey(key);
554 return false;
555 }
556
557 return true;
558 }
559
560 // wrapper function
561 bool SetRegValueMULTISZ(HANDLE key,
562 const wchar_t* value_name,
563 const std::vector<std::wstring>& values) {
564 std::vector<wchar_t> builder;
565
566 for (auto& string : values) {
567 // Just in case someone is passing in an illegal empty string
568 // (not allowed in REG_MULTI_SZ), ignore it.
569 if (!string.empty()) {
570 for (const wchar_t& w : string) {
571 builder.push_back(w);
572 }
573 builder.push_back(L'\0');
574 }
575 }
576 // Add second null terminator to end REG_MULTI_SZ.
577 builder.push_back(L'\0');
578 // Handle rare case where the vector passed in was empty,
579 // or only had an empty string.
580 if (builder.size() == 1)
581 builder.push_back(L'\0');
582
583 if (std::numeric_limits<DWORD>::max() < builder.size())
584 return false;
585
586 return SetRegKeyValue(
587 key, value_name, REG_MULTI_SZ, reinterpret_cast<BYTE*>(builder.data()),
588 (static_cast<DWORD>(builder.size()) + 1) * sizeof(wchar_t));
589 }
590
591 // wrapper function
592 bool SetRegValueMULTISZ(ROOT_KEY root,
593 const wchar_t* key_path,
594 const wchar_t* value_name,
595 const std::vector<std::wstring>& values) {
596 HANDLE key = INVALID_HANDLE_VALUE;
597
598 if (!OpenRegKey(root, key_path, KEY_SET_VALUE | KEY_WOW64_32KEY, &key, NULL))
599 return false;
600
601 if (!SetRegValueMULTISZ(key, value_name, values)) {
602 CloseRegKey(key);
603 return false;
604 }
605
606 return true;
607 }
608
609 //------------------------------------------------------------------------------
610 // Utils
611 //------------------------------------------------------------------------------
612
613 std::wstring GetCurrentUserSidString() {
614 if (!g_initialized)
615 InitNativeRegApi();
616
617 return g_current_user_sid_string;
618 }
619
620 }; // namespace nt
OLDNEW
« no previous file with comments | « chrome_elf/nt_registry/nt_registry.h ('k') | chrome_elf/nt_registry/nt_registry.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698