1 /* 2 * FreeLoader 3 * 4 * Copyright (C) 2014 Timo Kreuzer <timo.kreuzer@reactos.org> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License along 17 * with this program; if not, write to the Free Software Foundation, Inc., 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 */ 20 21 #include <freeldr.h> 22 #include <cmlib.h> 23 #include "registry.h" 24 25 #include <debug.h> 26 DBG_DEFAULT_CHANNEL(REGISTRY); 27 28 static PCMHIVE CmSystemHive; 29 static HCELL_INDEX SystemRootCell; 30 31 PHHIVE SystemHive = NULL; 32 HKEY CurrentControlSetKey = NULL; 33 34 #define GET_HHIVE(CmHive) (&((CmHive)->Hive)) 35 #define GET_HHIVE_FROM_HKEY(hKey) GET_HHIVE(CmSystemHive) 36 #define GET_CM_KEY_NODE(hHive, hKey) ((PCM_KEY_NODE)HvGetCell(hHive, (HCELL_INDEX)hKey)) 37 38 PVOID 39 NTAPI 40 CmpAllocate( 41 IN SIZE_T Size, 42 IN BOOLEAN Paged, 43 IN ULONG Tag) 44 { 45 UNREFERENCED_PARAMETER(Paged); 46 return FrLdrHeapAlloc(Size, Tag); 47 } 48 49 VOID 50 NTAPI 51 CmpFree( 52 IN PVOID Ptr, 53 IN ULONG Quota) 54 { 55 UNREFERENCED_PARAMETER(Quota); 56 FrLdrHeapFree(Ptr, 0); 57 } 58 59 BOOLEAN 60 RegImportBinaryHive( 61 _In_ PVOID ChunkBase, 62 _In_ ULONG ChunkSize) 63 { 64 NTSTATUS Status; 65 PCM_KEY_NODE KeyNode; 66 67 TRACE("RegImportBinaryHive(%p, 0x%lx)\n", ChunkBase, ChunkSize); 68 69 /* Allocate and initialize the hive */ 70 CmSystemHive = FrLdrTempAlloc(sizeof(CMHIVE), 'eviH'); 71 Status = HvInitialize(GET_HHIVE(CmSystemHive), 72 HINIT_FLAT, // HINIT_MEMORY_INPLACE 73 0, 74 0, 75 ChunkBase, 76 CmpAllocate, 77 CmpFree, 78 NULL, 79 NULL, 80 NULL, 81 NULL, 82 1, 83 NULL); 84 if (!NT_SUCCESS(Status)) 85 { 86 ERR("Corrupted hive %p!\n", ChunkBase); 87 FrLdrTempFree(CmSystemHive, 'eviH'); 88 return FALSE; 89 } 90 91 /* Save the root key node */ 92 SystemHive = GET_HHIVE(CmSystemHive); 93 SystemRootCell = SystemHive->BaseBlock->RootCell; 94 ASSERT(SystemRootCell != HCELL_NIL); 95 96 /* Verify it is accessible */ 97 KeyNode = (PCM_KEY_NODE)HvGetCell(SystemHive, SystemRootCell); 98 ASSERT(KeyNode); 99 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 100 HvReleaseCell(SystemHive, SystemRootCell); 101 102 TRACE("RegImportBinaryHive done\n"); 103 return TRUE; 104 } 105 106 LONG 107 RegInitCurrentControlSet( 108 _In_ BOOLEAN LastKnownGood) 109 { 110 WCHAR ControlSetKeyName[80]; 111 HKEY SelectKey; 112 HKEY SystemKey; 113 ULONG CurrentSet = 0; 114 ULONG DefaultSet = 0; 115 ULONG LastKnownGoodSet = 0; 116 ULONG DataSize; 117 LONG Error; 118 119 TRACE("RegInitCurrentControlSet\n"); 120 121 Error = RegOpenKey(NULL, 122 L"\\Registry\\Machine\\SYSTEM\\Select", 123 &SelectKey); 124 if (Error != ERROR_SUCCESS) 125 { 126 ERR("RegOpenKey('SYSTEM\\Select') failed (Error %lu)\n", Error); 127 return Error; 128 } 129 130 DataSize = sizeof(ULONG); 131 Error = RegQueryValue(SelectKey, 132 L"Default", 133 NULL, 134 (PUCHAR)&DefaultSet, 135 &DataSize); 136 if (Error != ERROR_SUCCESS) 137 { 138 ERR("RegQueryValue('Default') failed (Error %lu)\n", Error); 139 RegCloseKey(SelectKey); 140 return Error; 141 } 142 143 DataSize = sizeof(ULONG); 144 Error = RegQueryValue(SelectKey, 145 L"LastKnownGood", 146 NULL, 147 (PUCHAR)&LastKnownGoodSet, 148 &DataSize); 149 if (Error != ERROR_SUCCESS) 150 { 151 ERR("RegQueryValue('LastKnownGood') failed (Error %lu)\n", Error); 152 RegCloseKey(SelectKey); 153 return Error; 154 } 155 156 RegCloseKey(SelectKey); 157 158 CurrentSet = (LastKnownGood) ? LastKnownGoodSet : DefaultSet; 159 wcscpy(ControlSetKeyName, L"ControlSet"); 160 switch(CurrentSet) 161 { 162 case 1: 163 wcscat(ControlSetKeyName, L"001"); 164 break; 165 case 2: 166 wcscat(ControlSetKeyName, L"002"); 167 break; 168 case 3: 169 wcscat(ControlSetKeyName, L"003"); 170 break; 171 case 4: 172 wcscat(ControlSetKeyName, L"004"); 173 break; 174 case 5: 175 wcscat(ControlSetKeyName, L"005"); 176 break; 177 } 178 179 Error = RegOpenKey(NULL, 180 L"\\Registry\\Machine\\SYSTEM", 181 &SystemKey); 182 if (Error != ERROR_SUCCESS) 183 { 184 ERR("RegOpenKey('SYSTEM') failed (Error %lu)\n", Error); 185 return Error; 186 } 187 188 Error = RegOpenKey(SystemKey, 189 ControlSetKeyName, 190 &CurrentControlSetKey); 191 192 RegCloseKey(SystemKey); 193 194 if (Error != ERROR_SUCCESS) 195 { 196 ERR("RegOpenKey('%S') failed (Error %lu)\n", ControlSetKeyName, Error); 197 return Error; 198 } 199 200 TRACE("RegInitCurrentControlSet done\n"); 201 return ERROR_SUCCESS; 202 } 203 204 static 205 BOOLEAN 206 GetNextPathElement( 207 _Out_ PUNICODE_STRING NextElement, 208 _Inout_ PUNICODE_STRING RemainingPath) 209 { 210 /* Check if there are any characters left */ 211 if (RemainingPath->Length < sizeof(WCHAR)) 212 { 213 /* Nothing left, bail out early */ 214 return FALSE; 215 } 216 217 /* The next path elements starts with the remaining path */ 218 NextElement->Buffer = RemainingPath->Buffer; 219 220 /* Loop until the path element ends */ 221 while ((RemainingPath->Length >= sizeof(WCHAR)) && 222 (RemainingPath->Buffer[0] != '\\')) 223 { 224 /* Skip this character */ 225 RemainingPath->Buffer++; 226 RemainingPath->Length -= sizeof(WCHAR); 227 } 228 229 NextElement->Length = (USHORT)(RemainingPath->Buffer - NextElement->Buffer) * sizeof(WCHAR); 230 NextElement->MaximumLength = NextElement->Length; 231 232 /* Check if the path element ended with a path separator */ 233 if (RemainingPath->Length >= sizeof(WCHAR)) 234 { 235 /* Skip the path separator */ 236 ASSERT(RemainingPath->Buffer[0] == '\\'); 237 RemainingPath->Buffer++; 238 RemainingPath->Length -= sizeof(WCHAR); 239 } 240 241 /* Return whether we got any characters */ 242 return TRUE; 243 } 244 245 #if 0 246 LONG 247 RegEnumKey( 248 _In_ HKEY Key, 249 _In_ ULONG Index, 250 _Out_ PWCHAR Name, 251 _Inout_ PULONG NameSize, 252 _Out_opt_ PHKEY SubKey) 253 { 254 PHHIVE Hive = GET_HHIVE_FROM_HKEY(Key); 255 PCM_KEY_NODE KeyNode, SubKeyNode; 256 HCELL_INDEX CellIndex; 257 USHORT NameLength; 258 259 TRACE("RegEnumKey(%p, %lu, %p, %p->%u)\n", 260 Key, Index, Name, NameSize, NameSize ? *NameSize : 0); 261 262 /* Get the key node */ 263 KeyNode = GET_CM_KEY_NODE(Hive, Key); 264 ASSERT(KeyNode); 265 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 266 267 CellIndex = CmpFindSubKeyByNumber(Hive, KeyNode, Index); 268 if (CellIndex == HCELL_NIL) 269 { 270 TRACE("RegEnumKey index out of bounds (%d) in key (%.*s)\n", 271 Index, KeyNode->NameLength, KeyNode->Name); 272 HvReleaseCell(Hive, (HCELL_INDEX)Key); 273 return ERROR_NO_MORE_ITEMS; 274 } 275 HvReleaseCell(Hive, (HCELL_INDEX)Key); 276 277 /* Get the value cell */ 278 SubKeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex); 279 ASSERT(SubKeyNode != NULL); 280 ASSERT(SubKeyNode->Signature == CM_KEY_NODE_SIGNATURE); 281 282 if (SubKeyNode->Flags & KEY_COMP_NAME) 283 { 284 NameLength = CmpCompressedNameSize(SubKeyNode->Name, SubKeyNode->NameLength); 285 286 /* Compressed name */ 287 CmpCopyCompressedName(Name, 288 *NameSize, 289 SubKeyNode->Name, 290 SubKeyNode->NameLength); 291 } 292 else 293 { 294 NameLength = SubKeyNode->NameLength; 295 296 /* Normal name */ 297 RtlCopyMemory(Name, SubKeyNode->Name, 298 min(*NameSize, SubKeyNode->NameLength)); 299 } 300 301 if (*NameSize >= NameLength + sizeof(WCHAR)) 302 { 303 Name[NameLength / sizeof(WCHAR)] = UNICODE_NULL; 304 } 305 306 *NameSize = NameLength + sizeof(WCHAR); 307 308 HvReleaseCell(Hive, CellIndex); 309 310 if (SubKey != NULL) 311 *SubKey = (HKEY)CellIndex; 312 313 TRACE("RegEnumKey done -> %u, '%.*S'\n", *NameSize, *NameSize, Name); 314 return ERROR_SUCCESS; 315 } 316 #endif 317 318 LONG 319 RegOpenKey( 320 _In_ HKEY ParentKey, 321 _In_z_ PCWSTR KeyName, 322 _Out_ PHKEY Key) 323 { 324 UNICODE_STRING RemainingPath, SubKeyName; 325 UNICODE_STRING CurrentControlSet = RTL_CONSTANT_STRING(L"CurrentControlSet"); 326 PHHIVE Hive = (ParentKey ? GET_HHIVE_FROM_HKEY(ParentKey) : GET_HHIVE(CmSystemHive)); 327 PCM_KEY_NODE KeyNode; 328 HCELL_INDEX CellIndex; 329 330 TRACE("RegOpenKey(%p, '%S', %p)\n", ParentKey, KeyName, Key); 331 332 /* Initialize the remaining path name */ 333 RtlInitUnicodeString(&RemainingPath, KeyName); 334 335 /* Check if we have a parent key */ 336 if (ParentKey == NULL) 337 { 338 UNICODE_STRING SubKeyName1, SubKeyName2, SubKeyName3; 339 UNICODE_STRING RegistryPath = RTL_CONSTANT_STRING(L"Registry"); 340 UNICODE_STRING MachinePath = RTL_CONSTANT_STRING(L"MACHINE"); 341 UNICODE_STRING SystemPath = RTL_CONSTANT_STRING(L"SYSTEM"); 342 343 TRACE("RegOpenKey: absolute path\n"); 344 345 if ((RemainingPath.Length < sizeof(WCHAR)) || 346 RemainingPath.Buffer[0] != '\\') 347 { 348 /* The key path is not absolute */ 349 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath); 350 return ERROR_PATH_NOT_FOUND; 351 } 352 353 /* Skip initial path separator */ 354 RemainingPath.Buffer++; 355 RemainingPath.Length -= sizeof(WCHAR); 356 357 /* Get the first 3 path elements */ 358 GetNextPathElement(&SubKeyName1, &RemainingPath); 359 GetNextPathElement(&SubKeyName2, &RemainingPath); 360 GetNextPathElement(&SubKeyName3, &RemainingPath); 361 TRACE("RegOpenKey: %wZ / %wZ / %wZ\n", &SubKeyName1, &SubKeyName2, &SubKeyName3); 362 363 /* Check if we have the correct path */ 364 if (!RtlEqualUnicodeString(&SubKeyName1, &RegistryPath, TRUE) || 365 !RtlEqualUnicodeString(&SubKeyName2, &MachinePath, TRUE) || 366 !RtlEqualUnicodeString(&SubKeyName3, &SystemPath, TRUE)) 367 { 368 /* The key path is not inside HKLM\Machine\System */ 369 ERR("RegOpenKey: invalid path '%S' (%wZ)\n", KeyName, &RemainingPath); 370 return ERROR_PATH_NOT_FOUND; 371 } 372 373 /* Use the root key */ 374 CellIndex = SystemRootCell; 375 } 376 else 377 { 378 /* Use the parent key */ 379 CellIndex = (HCELL_INDEX)ParentKey; 380 } 381 382 /* Check if this is the root key */ 383 if (CellIndex == SystemRootCell) 384 { 385 UNICODE_STRING TempPath = RemainingPath; 386 387 /* Get the first path element */ 388 GetNextPathElement(&SubKeyName, &TempPath); 389 390 /* Check if this is CurrentControlSet */ 391 if (RtlEqualUnicodeString(&SubKeyName, &CurrentControlSet, TRUE)) 392 { 393 /* Use the CurrentControlSetKey and update the remaining path */ 394 CellIndex = (HCELL_INDEX)CurrentControlSetKey; 395 RemainingPath = TempPath; 396 } 397 } 398 399 /* Get the key node */ 400 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex); 401 ASSERT(KeyNode); 402 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 403 404 TRACE("RegOpenKey: RemainingPath '%wZ'\n", &RemainingPath); 405 406 /* Loop while there are path elements */ 407 while (GetNextPathElement(&SubKeyName, &RemainingPath)) 408 { 409 HCELL_INDEX NextCellIndex; 410 411 TRACE("RegOpenKey: next element '%wZ'\n", &SubKeyName); 412 413 /* Get the next sub key */ 414 NextCellIndex = CmpFindSubKeyByName(Hive, KeyNode, &SubKeyName); 415 HvReleaseCell(Hive, CellIndex); 416 CellIndex = NextCellIndex; 417 if (CellIndex == HCELL_NIL) 418 { 419 WARN("Did not find sub key '%wZ' (full: %S)\n", &SubKeyName, KeyName); 420 return ERROR_PATH_NOT_FOUND; 421 } 422 423 /* Get the found key */ 424 KeyNode = (PCM_KEY_NODE)HvGetCell(Hive, CellIndex); 425 ASSERT(KeyNode); 426 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 427 } 428 429 HvReleaseCell(Hive, CellIndex); 430 *Key = (HKEY)CellIndex; 431 432 TRACE("RegOpenKey done\n"); 433 return ERROR_SUCCESS; 434 } 435 436 static 437 VOID 438 RepGetValueData( 439 _In_ PHHIVE Hive, 440 _In_ PCM_KEY_VALUE ValueCell, 441 _Out_opt_ PULONG Type, 442 _Out_opt_ PUCHAR Data, 443 _Inout_opt_ PULONG DataSize) 444 { 445 ULONG DataLength; 446 PVOID DataCell; 447 448 /* Does the caller want the type? */ 449 if (Type != NULL) 450 *Type = ValueCell->Type; 451 452 /* Does the caller provide DataSize? */ 453 if (DataSize != NULL) 454 { 455 // NOTE: CmpValueToData doesn't support big data (the function will 456 // bugcheck if so), FreeLdr is not supposed to read such data. 457 // If big data is needed, use instead CmpGetValueData. 458 // CmpGetValueData(Hive, ValueCell, DataSize, &DataCell, ...); 459 DataCell = CmpValueToData(Hive, ValueCell, &DataLength); 460 461 /* Does the caller want the data? */ 462 if ((Data != NULL) && (*DataSize != 0)) 463 { 464 RtlCopyMemory(Data, 465 DataCell, 466 min(*DataSize, DataLength)); 467 } 468 469 /* Return the actual data length */ 470 *DataSize = DataLength; 471 } 472 } 473 474 LONG 475 RegQueryValue( 476 _In_ HKEY Key, 477 _In_z_ PCWSTR ValueName, 478 _Out_opt_ PULONG Type, 479 _Out_opt_ PUCHAR Data, 480 _Inout_opt_ PULONG DataSize) 481 { 482 PHHIVE Hive = GET_HHIVE_FROM_HKEY(Key); 483 PCM_KEY_NODE KeyNode; 484 PCM_KEY_VALUE ValueCell; 485 HCELL_INDEX CellIndex; 486 UNICODE_STRING ValueNameString; 487 488 TRACE("RegQueryValue(%p, '%S', %p, %p, %p)\n", 489 Key, ValueName, Type, Data, DataSize); 490 491 /* Get the key node */ 492 KeyNode = GET_CM_KEY_NODE(Hive, Key); 493 ASSERT(KeyNode); 494 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 495 496 /* Initialize value name string */ 497 RtlInitUnicodeString(&ValueNameString, ValueName); 498 CellIndex = CmpFindValueByName(Hive, KeyNode, &ValueNameString); 499 if (CellIndex == HCELL_NIL) 500 { 501 TRACE("RegQueryValue value not found in key (%.*s)\n", 502 KeyNode->NameLength, KeyNode->Name); 503 HvReleaseCell(Hive, (HCELL_INDEX)Key); 504 return ERROR_FILE_NOT_FOUND; 505 } 506 HvReleaseCell(Hive, (HCELL_INDEX)Key); 507 508 /* Get the value cell */ 509 ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, CellIndex); 510 ASSERT(ValueCell != NULL); 511 512 RepGetValueData(Hive, ValueCell, Type, Data, DataSize); 513 514 HvReleaseCell(Hive, CellIndex); 515 516 TRACE("RegQueryValue success\n"); 517 return ERROR_SUCCESS; 518 } 519 520 /* 521 * NOTE: This function is currently unused in FreeLdr; however it is kept here 522 * as an implementation reference of RegEnumValue using CMLIB that may be used 523 * elsewhere in ReactOS. 524 */ 525 #if 0 526 LONG 527 RegEnumValue( 528 _In_ HKEY Key, 529 _In_ ULONG Index, 530 _Out_ PWCHAR ValueName, 531 _Inout_ PULONG NameSize, 532 _Out_opt_ PULONG Type, 533 _Out_opt_ PUCHAR Data, 534 _Inout_opt_ PULONG DataSize) 535 { 536 PHHIVE Hive = GET_HHIVE_FROM_HKEY(Key); 537 PCM_KEY_NODE KeyNode; 538 PCELL_DATA ValueListCell; 539 PCM_KEY_VALUE ValueCell; 540 USHORT NameLength; 541 542 TRACE("RegEnumValue(%p, %lu, %S, %p, %p, %p, %p (%lu))\n", 543 Key, Index, ValueName, NameSize, Type, Data, DataSize, *DataSize); 544 545 /* Get the key node */ 546 KeyNode = GET_CM_KEY_NODE(Hive, Key); 547 ASSERT(KeyNode); 548 ASSERT(KeyNode->Signature == CM_KEY_NODE_SIGNATURE); 549 550 /* Check if the index is valid */ 551 if ((KeyNode->ValueList.Count == 0) || 552 (KeyNode->ValueList.List == HCELL_NIL) || 553 (Index >= KeyNode->ValueList.Count)) 554 { 555 ERR("RegEnumValue: index invalid\n"); 556 HvReleaseCell(Hive, (HCELL_INDEX)Key); 557 return ERROR_NO_MORE_ITEMS; 558 } 559 560 ValueListCell = (PCELL_DATA)HvGetCell(Hive, KeyNode->ValueList.List); 561 ASSERT(ValueListCell != NULL); 562 563 /* Get the value cell */ 564 ValueCell = (PCM_KEY_VALUE)HvGetCell(Hive, ValueListCell->KeyList[Index]); 565 ASSERT(ValueCell != NULL); 566 ASSERT(ValueCell->Signature == CM_KEY_VALUE_SIGNATURE); 567 568 if (ValueCell->Flags & VALUE_COMP_NAME) 569 { 570 NameLength = CmpCompressedNameSize(ValueCell->Name, ValueCell->NameLength); 571 572 /* Compressed name */ 573 CmpCopyCompressedName(ValueName, 574 *NameSize, 575 ValueCell->Name, 576 ValueCell->NameLength); 577 } 578 else 579 { 580 NameLength = ValueCell->NameLength; 581 582 /* Normal name */ 583 RtlCopyMemory(ValueName, ValueCell->Name, 584 min(*NameSize, ValueCell->NameLength)); 585 } 586 587 if (*NameSize >= NameLength + sizeof(WCHAR)) 588 { 589 ValueName[NameLength / sizeof(WCHAR)] = UNICODE_NULL; 590 } 591 592 *NameSize = NameLength + sizeof(WCHAR); 593 594 RepGetValueData(Hive, ValueCell, Type, Data, DataSize); 595 596 HvReleaseCell(Hive, ValueListCell->KeyList[Index]); 597 HvReleaseCell(Hive, KeyNode->ValueList.List); 598 HvReleaseCell(Hive, (HCELL_INDEX)Key); 599 600 TRACE("RegEnumValue done -> %u, '%.*S'\n", *NameSize, *NameSize, ValueName); 601 return ERROR_SUCCESS; 602 } 603 #endif 604 605 /* EOF */ 606