1 /*
2  * PROJECT:         EFI Windows Loader
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            boot/freeldr/freeldr/windows/wlregistry.c
5  * PURPOSE:         Registry support functions
6  * PROGRAMMERS:     Aleksey Bragin (aleksey@reactos.org)
7  */
8 
9 /* INCLUDES ***************************************************************/
10 
11 #include <freeldr.h>
12 #include "winldr.h"
13 #include "registry.h"
14 #include <internal/cmboot.h>
15 
16 #include <debug.h>
17 DBG_DEFAULT_CHANNEL(WINDOWS);
18 
19 // The only global var here, used to mark mem pages as NLS in WinLdrSetupMemoryLayout()
20 ULONG TotalNLSSize = 0;
21 
22 static BOOLEAN
23 WinLdrGetNLSNames(
24     _In_ HKEY ControlSet,
25     _Inout_ PUNICODE_STRING AnsiFileName,
26     _Inout_ PUNICODE_STRING OemFileName,
27     _Inout_ PUNICODE_STRING LangFileName, // CaseTable
28     _Inout_ PUNICODE_STRING OemHalFileName);
29 
30 static BOOLEAN
31 WinLdrScanRegistry(
32     IN OUT PLIST_ENTRY BootDriverListHead);
33 
34 typedef enum _BAD_HIVE_REASON
35 {
36     GoodHive = 1,
37     CorruptHive,
38     NoHive,
39     NoHiveAlloc
40 } BAD_HIVE_REASON, *PBAD_HIVE_REASON;
41 
42 /* FUNCTIONS **************************************************************/
43 
44 static BOOLEAN
WinLdrLoadSystemHive(_Inout_ PLOADER_PARAMETER_BLOCK LoaderBlock,_In_ PCSTR DirectoryPath,_In_ PCSTR HiveName,_Out_ PBAD_HIVE_REASON Reason)45 WinLdrLoadSystemHive(
46     _Inout_ PLOADER_PARAMETER_BLOCK LoaderBlock,
47     _In_ PCSTR DirectoryPath,
48     _In_ PCSTR HiveName,
49     _Out_ PBAD_HIVE_REASON Reason)
50 {
51     ULONG FileId;
52     CHAR FullHiveName[MAX_PATH];
53     ARC_STATUS Status;
54     FILEINFORMATION FileInfo;
55     ULONG HiveFileSize;
56     PVOID HiveDataPhysical;
57     PVOID HiveDataVirtual;
58     ULONG BytesRead;
59 
60     /* Do not setup any bad reason for now */
61     *Reason = GoodHive;
62 
63     /* Concatenate path and filename to get the full name */
64     RtlStringCbCopyA(FullHiveName, sizeof(FullHiveName), DirectoryPath);
65     RtlStringCbCatA(FullHiveName, sizeof(FullHiveName), HiveName);
66 
67     NtLdrOutputLoadMsg(FullHiveName, NULL);
68     Status = ArcOpen(FullHiveName, OpenReadOnly, &FileId);
69     if (Status != ESUCCESS)
70     {
71         WARN("Error while opening '%s', Status: %u\n", FullHiveName, Status);
72         *Reason = NoHive;
73         return FALSE;
74     }
75 
76     /* Get the file length */
77     Status = ArcGetFileInformation(FileId, &FileInfo);
78     if (Status != ESUCCESS)
79     {
80         WARN("Hive file has 0 size!\n");
81         *Reason = CorruptHive;
82         ArcClose(FileId);
83         return FALSE;
84     }
85     HiveFileSize = FileInfo.EndingAddress.LowPart;
86 
87     /* Round up the size to page boundary and alloc memory */
88     HiveDataPhysical = MmAllocateMemoryWithType(
89         MM_SIZE_TO_PAGES(HiveFileSize + MM_PAGE_SIZE - 1) << MM_PAGE_SHIFT,
90         LoaderRegistryData);
91 
92     if (HiveDataPhysical == NULL)
93     {
94         WARN("Could not alloc memory for hive!\n");
95         *Reason = NoHiveAlloc;
96         ArcClose(FileId);
97         return FALSE;
98     }
99 
100     /* Convert address to virtual */
101     HiveDataVirtual = PaToVa(HiveDataPhysical);
102 
103     /* Fill LoaderBlock's entries */
104     LoaderBlock->RegistryLength = HiveFileSize;
105     LoaderBlock->RegistryBase = HiveDataVirtual;
106 
107     /* Finally read from file to the memory */
108     Status = ArcRead(FileId, HiveDataPhysical, HiveFileSize, &BytesRead);
109     if (Status != ESUCCESS)
110     {
111         WARN("Error while reading '%s', Status: %u\n", FullHiveName, Status);
112         *Reason = CorruptHive;
113         ArcClose(FileId);
114         return FALSE;
115     }
116 
117     // FIXME: HACK: Get the boot filesystem driver name now...
118     BootFileSystem = FsGetServiceName(FileId);
119 
120     ArcClose(FileId);
121     return TRUE;
122 }
123 
124 BOOLEAN
WinLdrInitSystemHive(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,IN PCSTR SystemRoot,IN BOOLEAN Setup)125 WinLdrInitSystemHive(
126     IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
127     IN PCSTR SystemRoot,
128     IN BOOLEAN Setup)
129 {
130     CHAR SearchPath[1024];
131     PVOID ChunkBase;
132     PCSTR HiveName;
133     BOOLEAN Success;
134     BAD_HIVE_REASON Reason;
135 
136     /* Load the corresponding text-mode setup system hive or the standard hive */
137     if (Setup)
138     {
139         RtlStringCbCopyA(SearchPath, sizeof(SearchPath), SystemRoot);
140         HiveName = "SETUPREG.HIV";
141     }
142     else
143     {
144         RtlStringCbCopyA(SearchPath, sizeof(SearchPath), SystemRoot);
145         RtlStringCbCatA(SearchPath, sizeof(SearchPath), "system32\\config\\");
146         HiveName = "SYSTEM";
147     }
148 
149     TRACE("WinLdrInitSystemHive: loading hive %s%s\n", SearchPath, HiveName);
150     Success = WinLdrLoadSystemHive(LoaderBlock, SearchPath, HiveName, &Reason);
151     if (!Success)
152     {
153         /* Check whether the SYSTEM hive does not exist or is too corrupt to be read */
154         if (Reason == CorruptHive || Reason == NoHive)
155         {
156             /* Try loading the alternate hive, the main hive should be recovered later */
157             goto LoadAlternateHive;
158         }
159 
160         /* We are failing for other reason, bail out */
161         UiMessageBox("Could not load %s hive!", HiveName);
162         return FALSE;
163     }
164 
165     /* Import what was loaded */
166     Success = RegImportBinaryHive(VaToPa(LoaderBlock->RegistryBase), LoaderBlock->RegistryLength, SearchPath, FALSE);
167     if (!Success)
168     {
169         /*
170          * Importing of the SYSTEM hive failed. The scenarios that would
171          * have made this possible are the following:
172          *
173          * 1. The primary hive is corrupt beyond repair (such as when
174          *    core FS structures are total toast);
175          *
176          * 2. Repairing the hive could with a LOG could not recover it
177          *    to the fullest. This is the case when the hive and LOG have
178          *    diverged too much, or are mismatched, or the containing healthy
179          *    data in the LOG was not marked as dirty that could be copied
180          *    into the primary hive;
181          *
182          * 3. LOG is bad (e.g. corrupt dirty vector);
183          *
184          * 4. LOG does not physically exist on the backing storage.
185          *
186          * 5. SYSTEM hive does not physically exist or it is a 0 bytes file
187          *    (the latter case still counts as corruption).
188          *
189          * With the hope to boot the system, load the mirror counterpart
190          * of the main hive, the alternate. The kernel should be able to recover
191          * the main hive later on as soon as it starts writing to it.
192          */
193 LoadAlternateHive:
194         HiveName = "SYSTEM.ALT";
195         Success = WinLdrLoadSystemHive(LoaderBlock, SearchPath, HiveName, &Reason);
196         if (!Success)
197         {
198             UiMessageBox("Could not load %s hive!", HiveName);
199             return FALSE;
200         }
201 
202         /* Retry importing it again */
203         Success = RegImportBinaryHive(VaToPa(LoaderBlock->RegistryBase), LoaderBlock->RegistryLength, SearchPath, TRUE);
204         if (!Success)
205         {
206             UiMessageBox("Importing binary hive failed!");
207             return FALSE;
208         }
209 
210         /*
211          * Acknowledge the kernel we recovered the SYSTEM hive
212          * on our side by loading the alternate variant of the hive.
213          */
214         WARN("SYSTEM hive does not exist or is corrupt and SYSTEM.ALT has been loaded!\n");
215         ChunkBase = VaToPa(LoaderBlock->RegistryBase);
216         ((PHBASE_BLOCK)ChunkBase)->BootRecover = HBOOT_BOOT_RECOVERED_BY_ALTERNATE_HIVE;
217     }
218 
219     // FIXME: Load SYSTEM.SAV if GUI setup installation is still in progress
220 
221     /* Initialize the 'CurrentControlSet' link */
222     if (!RegInitCurrentControlSet(FALSE))
223     {
224         UiMessageBox("Initializing CurrentControlSet link failed!");
225         return FALSE;
226     }
227 
228     return TRUE;
229 }
230 
WinLdrScanSystemHive(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,IN PCSTR SystemRoot)231 BOOLEAN WinLdrScanSystemHive(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock,
232                              IN PCSTR SystemRoot)
233 {
234     BOOLEAN Success;
235     DECLARE_UNICODE_STRING_SIZE(AnsiFileName, MAX_PATH);
236     DECLARE_UNICODE_STRING_SIZE(OemFileName, MAX_PATH);
237     DECLARE_UNICODE_STRING_SIZE(LangFileName, MAX_PATH); // CaseTable
238     DECLARE_UNICODE_STRING_SIZE(OemHalFileName, MAX_PATH);
239     CHAR SearchPath[1024];
240 
241     /* Scan registry and prepare boot drivers list */
242     Success = WinLdrScanRegistry(&LoaderBlock->BootDriverListHead);
243     if (!Success)
244     {
245         UiMessageBox("Failed to load boot drivers!");
246         return FALSE;
247     }
248 
249     /* Get names of NLS files */
250     Success = WinLdrGetNLSNames(CurrentControlSetKey,
251                                 &AnsiFileName,
252                                 &OemFileName,
253                                 &LangFileName,
254                                 &OemHalFileName);
255     if (!Success)
256     {
257         UiMessageBox("Getting NLS names from registry failed!");
258         return FALSE;
259     }
260 
261     TRACE("NLS data: '%wZ' '%wZ' '%wZ' '%wZ'\n",
262           &AnsiFileName, &OemFileName, &LangFileName, &OemHalFileName);
263 
264     /* Load NLS data */
265     RtlStringCbCopyA(SearchPath, sizeof(SearchPath), SystemRoot);
266     RtlStringCbCatA(SearchPath, sizeof(SearchPath), "system32\\");
267     Success = WinLdrLoadNLSData(LoaderBlock,
268                                 SearchPath,
269                                 &AnsiFileName,
270                                 &OemFileName,
271                                 &LangFileName,
272                                 &OemHalFileName);
273     TRACE("NLS data loading %s\n", Success ? "successful" : "failed");
274 
275     return TRUE;
276 }
277 
278 
279 /* PRIVATE FUNCTIONS ******************************************************/
280 
281 // Queries registry for those three file names
282 static BOOLEAN
WinLdrGetNLSNames(_In_ HKEY ControlSet,_Inout_ PUNICODE_STRING AnsiFileName,_Inout_ PUNICODE_STRING OemFileName,_Inout_ PUNICODE_STRING LangFileName,_Inout_ PUNICODE_STRING OemHalFileName)283 WinLdrGetNLSNames(
284     _In_ HKEY ControlSet,
285     _Inout_ PUNICODE_STRING AnsiFileName,
286     _Inout_ PUNICODE_STRING OemFileName,
287     _Inout_ PUNICODE_STRING LangFileName, // CaseTable
288     _Inout_ PUNICODE_STRING OemHalFileName)
289 {
290     LONG rc;
291     HKEY hKey;
292     ULONG BufferSize;
293     WCHAR szIdBuffer[80];
294 
295     /* Open the CodePage key */
296     rc = RegOpenKey(ControlSet, L"Control\\NLS\\CodePage", &hKey);
297     if (rc != ERROR_SUCCESS)
298     {
299         //TRACE("Couldn't open CodePage registry key\n");
300         return FALSE;
301     }
302 
303     /* Get ANSI codepage file */
304     BufferSize = sizeof(szIdBuffer);
305     rc = RegQueryValue(hKey, L"ACP", NULL, (PUCHAR)szIdBuffer, &BufferSize);
306     if (rc != ERROR_SUCCESS)
307     {
308         //TRACE("Couldn't get ACP NLS setting\n");
309         goto Quit;
310     }
311 
312     BufferSize = AnsiFileName->MaximumLength;
313     rc = RegQueryValue(hKey, szIdBuffer, NULL,
314                        (PUCHAR)AnsiFileName->Buffer, &BufferSize);
315     if (rc != ERROR_SUCCESS)
316     {
317         //TRACE("ACP NLS Setting exists, but isn't readable\n");
318         //goto Quit;
319         AnsiFileName->Length = 0;
320         RtlAppendUnicodeToString(AnsiFileName, L"c_1252.nls"); // HACK: ReactOS bug CORE-6105
321     }
322     else
323     {
324         AnsiFileName->Length = (USHORT)BufferSize - sizeof(UNICODE_NULL);
325     }
326 
327     /* Get OEM codepage file */
328     BufferSize = sizeof(szIdBuffer);
329     rc = RegQueryValue(hKey, L"OEMCP", NULL, (PUCHAR)szIdBuffer, &BufferSize);
330     if (rc != ERROR_SUCCESS)
331     {
332         //TRACE("Couldn't get OEMCP NLS setting\n");
333         goto Quit;
334     }
335 
336     BufferSize = OemFileName->MaximumLength;
337     rc = RegQueryValue(hKey, szIdBuffer, NULL,
338                        (PUCHAR)OemFileName->Buffer, &BufferSize);
339     if (rc != ERROR_SUCCESS)
340     {
341         //TRACE("OEMCP NLS setting exists, but isn't readable\n");
342         //goto Quit;
343         OemFileName->Length = 0;
344         RtlAppendUnicodeToString(OemFileName, L"c_437.nls"); // HACK: ReactOS bug CORE-6105
345     }
346     else
347     {
348         OemFileName->Length = (USHORT)BufferSize - sizeof(UNICODE_NULL);
349     }
350 
351     /* Get OEM HAL font file */
352     BufferSize = OemHalFileName->MaximumLength;
353     rc = RegQueryValue(hKey, L"OEMHAL", NULL,
354                        (PUCHAR)OemHalFileName->Buffer, &BufferSize);
355     if (rc != ERROR_SUCCESS)
356     {
357         //TRACE("Couldn't get OEMHAL NLS setting\n");
358         //goto Quit;
359         RtlInitEmptyUnicodeString(OemHalFileName, NULL, 0);
360     }
361     else
362     {
363         OemHalFileName->Length = (USHORT)BufferSize - sizeof(UNICODE_NULL);
364     }
365 
366     RegCloseKey(hKey);
367 
368     /* Open the Language key */
369     rc = RegOpenKey(ControlSet, L"Control\\NLS\\Language", &hKey);
370     if (rc != ERROR_SUCCESS)
371     {
372         //TRACE("Couldn't open Language registry key\n");
373         return FALSE;
374     }
375 
376     /* Get the Unicode case table file */
377     BufferSize = sizeof(szIdBuffer);
378     rc = RegQueryValue(hKey, L"Default", NULL, (PUCHAR)szIdBuffer, &BufferSize);
379     if (rc != ERROR_SUCCESS)
380     {
381         //TRACE("Couldn't get Language Default setting\n");
382         goto Quit;
383     }
384 
385     BufferSize = LangFileName->MaximumLength;
386     rc = RegQueryValue(hKey, szIdBuffer, NULL,
387                        (PUCHAR)LangFileName->Buffer, &BufferSize);
388     if (rc != ERROR_SUCCESS)
389     {
390         //TRACE("Language Default setting exists, but isn't readable\n");
391         //goto Quit;
392         LangFileName->Length = 0;
393         RtlAppendUnicodeToString(LangFileName, L"l_intl.nls");
394     }
395     else
396     {
397         LangFileName->Length = (USHORT)BufferSize - sizeof(UNICODE_NULL);
398     }
399 
400 Quit:
401     RegCloseKey(hKey);
402     return (rc == ERROR_SUCCESS);
403 }
404 
405 BOOLEAN
WinLdrLoadNLSData(_Inout_ PLOADER_PARAMETER_BLOCK LoaderBlock,_In_ PCSTR DirectoryPath,_In_ PCUNICODE_STRING AnsiFileName,_In_ PCUNICODE_STRING OemFileName,_In_ PCUNICODE_STRING LangFileName,_In_ PCUNICODE_STRING OemHalFileName)406 WinLdrLoadNLSData(
407     _Inout_ PLOADER_PARAMETER_BLOCK LoaderBlock,
408     _In_ PCSTR DirectoryPath,
409     _In_ PCUNICODE_STRING AnsiFileName,
410     _In_ PCUNICODE_STRING OemFileName,
411     _In_ PCUNICODE_STRING LangFileName, // CaseTable
412     _In_ PCUNICODE_STRING OemHalFileName)
413 {
414     ARC_STATUS Status;
415     FILEINFORMATION FileInfo;
416     ULONG AnsiFileId = -1, OemFileId = -1, LangFileId = -1;
417     ULONG AnsiFileSize, OemFileSize, LangFileSize;
418     ULONG TotalSize;
419     ULONG BytesRead;
420     PVOID NlsDataBase, NlsVirtual;
421     BOOLEAN AnsiEqualsOem = FALSE;
422     CHAR FileName[MAX_PATH];
423 
424     /* There may be a case, where OEM and ANSI pages coincide */
425     if (RtlCompareUnicodeString(AnsiFileName, OemFileName, TRUE) == 0)
426         AnsiEqualsOem = TRUE;
427 
428     /* Open file with ANSI and store its size */
429     RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
430                        DirectoryPath, AnsiFileName);
431     Status = ArcOpen(FileName, OpenReadOnly, &AnsiFileId);
432     if (Status != ESUCCESS)
433     {
434         WARN("Error while opening '%s', Status: %u\n", FileName, Status);
435         goto Quit;
436     }
437 
438     Status = ArcGetFileInformation(AnsiFileId, &FileInfo);
439     if (Status != ESUCCESS)
440         goto Quit;
441     AnsiFileSize = FileInfo.EndingAddress.LowPart;
442     TRACE("AnsiFileSize: %d\n", AnsiFileSize);
443 
444     /* Open OEM file and store its length */
445     if (AnsiEqualsOem)
446     {
447         OemFileSize = 0;
448     }
449     else
450     {
451         RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
452                            DirectoryPath, OemFileName);
453         Status = ArcOpen(FileName, OpenReadOnly, &OemFileId);
454         if (Status != ESUCCESS)
455         {
456             WARN("Error while opening '%s', Status: %u\n", FileName, Status);
457             goto Quit;
458         }
459 
460         Status = ArcGetFileInformation(OemFileId, &FileInfo);
461         if (Status != ESUCCESS)
462             goto Quit;
463         OemFileSize = FileInfo.EndingAddress.LowPart;
464     }
465     TRACE("OemFileSize: %d\n", OemFileSize);
466 
467     /* Finally open the language codepage file and store its length */
468     RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
469                        DirectoryPath, LangFileName);
470     Status = ArcOpen(FileName, OpenReadOnly, &LangFileId);
471     if (Status != ESUCCESS)
472     {
473         WARN("Error while opening '%s', Status: %u\n", FileName, Status);
474         goto Quit;
475     }
476 
477     Status = ArcGetFileInformation(LangFileId, &FileInfo);
478     if (Status != ESUCCESS)
479         goto Quit;
480     LangFileSize = FileInfo.EndingAddress.LowPart;
481     TRACE("LangFileSize: %d\n", LangFileSize);
482 
483     //
484     // TODO: The OEMHAL file.
485     //
486 
487     /* Sum up all three length, having in mind that every one of them
488        must start at a page boundary => thus round up each file to a page */
489     TotalSize = MM_SIZE_TO_PAGES(AnsiFileSize) +
490                 MM_SIZE_TO_PAGES(OemFileSize)  +
491                 MM_SIZE_TO_PAGES(LangFileSize);
492 
493     /* Store it for later marking the pages as NlsData type */
494     TotalNLSSize = TotalSize;
495 
496     NlsDataBase = MmAllocateMemoryWithType(TotalSize*MM_PAGE_SIZE, LoaderNlsData);
497     if (NlsDataBase == NULL)
498         goto Quit;
499 
500     NlsVirtual = PaToVa(NlsDataBase);
501     LoaderBlock->NlsData->AnsiCodePageData = NlsVirtual;
502 
503     LoaderBlock->NlsData->OemCodePageData =
504         (PVOID)((ULONG_PTR)NlsVirtual +
505         (MM_SIZE_TO_PAGES(AnsiFileSize) << MM_PAGE_SHIFT));
506 
507     LoaderBlock->NlsData->UnicodeCodePageData =
508         (PVOID)((ULONG_PTR)NlsVirtual +
509         (MM_SIZE_TO_PAGES(AnsiFileSize) << MM_PAGE_SHIFT) +
510         (MM_SIZE_TO_PAGES(OemFileSize)  << MM_PAGE_SHIFT));
511 
512     /* ANSI and OEM data are the same - just set pointers to the same area */
513     if (AnsiEqualsOem)
514         LoaderBlock->NlsData->OemCodePageData = LoaderBlock->NlsData->AnsiCodePageData;
515 
516     /* Now actually read the data into memory, starting with the ANSI file */
517     RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
518                        DirectoryPath, AnsiFileName);
519     NtLdrOutputLoadMsg(FileName, NULL);
520     Status = ArcRead(AnsiFileId,
521                      VaToPa(LoaderBlock->NlsData->AnsiCodePageData),
522                      AnsiFileSize, &BytesRead);
523     if (Status != ESUCCESS)
524     {
525         WARN("Error while reading '%s', Status: %u\n", FileName, Status);
526         goto Quit;
527     }
528 
529     /* OEM now, if it isn't the same as the ANSI one */
530     if (!AnsiEqualsOem)
531     {
532         RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
533                            DirectoryPath, OemFileName);
534         NtLdrOutputLoadMsg(FileName, NULL);
535         Status = ArcRead(OemFileId,
536                          VaToPa(LoaderBlock->NlsData->OemCodePageData),
537                          OemFileSize, &BytesRead);
538         if (Status != ESUCCESS)
539         {
540             WARN("Error while reading '%s', Status: %u\n", FileName, Status);
541             goto Quit;
542         }
543     }
544 
545     /* Finally the language file */
546     RtlStringCbPrintfA(FileName, sizeof(FileName), "%s%wZ",
547                        DirectoryPath, LangFileName);
548     NtLdrOutputLoadMsg(FileName, NULL);
549     Status = ArcRead(LangFileId,
550                      VaToPa(LoaderBlock->NlsData->UnicodeCodePageData),
551                      LangFileSize, &BytesRead);
552     if (Status != ESUCCESS)
553     {
554         WARN("Error while reading '%s', Status: %u\n", FileName, Status);
555         goto Quit;
556     }
557 
558     //
559     // THIS IS a HACK and should be replaced by actually loading the OEMHAL file!
560     //
561     LoaderBlock->OemFontFile = VaToPa(LoaderBlock->NlsData->UnicodeCodePageData);
562 
563     /* Convert NlsTables address to VA */
564     LoaderBlock->NlsData = PaToVa(LoaderBlock->NlsData);
565 
566 Quit:
567     if (LangFileId != -1)
568         ArcClose(LangFileId);
569     if (OemFileId != -1)
570         ArcClose(OemFileId);
571     if (AnsiFileId != -1)
572         ArcClose(AnsiFileId);
573 
574     if (Status != ESUCCESS)
575         UiMessageBox("Error reading NLS file %s", FileName);
576 
577     return (Status == ESUCCESS);
578 }
579 
580 static BOOLEAN
WinLdrScanRegistry(IN OUT PLIST_ENTRY BootDriverListHead)581 WinLdrScanRegistry(
582     IN OUT PLIST_ENTRY BootDriverListHead)
583 {
584     BOOLEAN Success;
585 
586     /* Find all boot drivers */
587     Success = CmpFindDrivers(SystemHive,
588                              HKEY_TO_HCI(CurrentControlSetKey),
589                              BootLoad,
590                              BootFileSystem,
591                              BootDriverListHead);
592     if (!Success)
593         goto Quit;
594 
595     /* Sort by group/tag */
596     Success = CmpSortDriverList(SystemHive,
597                                 HKEY_TO_HCI(CurrentControlSetKey),
598                                 BootDriverListHead);
599     if (!Success)
600         goto Quit;
601 
602     /* Remove circular dependencies (cycles) and sort */
603     Success = CmpResolveDriverDependencies(BootDriverListHead);
604     if (!Success)
605         goto Quit;
606 
607 Quit:
608     /* In case of failure, free the boot driver list */
609     if (!Success)
610         CmpFreeDriverList(SystemHive, BootDriverListHead);
611 
612     return Success;
613 }
614 
615 /**
616  * @brief
617  * Inserts the specified driver entry into the driver list, or updates
618  * an existing entry with new ImagePath, ErrorControl, Group and Tag values.
619  *
620  * @param[in,out]   DriverListHead
621  * The driver list where to insert the driver entry.
622  *
623  * @param[in]   InsertAtHead
624  * Whether to insert the driver at the head (TRUE) or at the tail (FALSE)
625  * of the driver list.
626  *
627  * @param[in]   DriverName
628  * The driver's name.
629  *
630  * @param[in]   ImagePath
631  * Optional path the the driver's image. If none is specified,
632  * a default path is constructed out of the driver's name.
633  *
634  * @param[in]   GroupName
635  * Optional driver group name.
636  *
637  * @param[in]   ErrorControl
638  * @param[in]   Tag
639  * The ErrorControl and group Tag values for the driver.
640  *
641  * @return
642  * TRUE if the driver has been inserted into the list or updated, FALSE if not.
643  **/
644 BOOLEAN
WinLdrAddDriverToList(_Inout_ PLIST_ENTRY DriverListHead,_In_ BOOLEAN InsertAtHead,_In_ PCWSTR DriverName,_In_opt_ PCWSTR ImagePath,_In_opt_ PCWSTR GroupName,_In_ ULONG ErrorControl,_In_ ULONG Tag)645 WinLdrAddDriverToList(
646     _Inout_ PLIST_ENTRY DriverListHead,
647     _In_ BOOLEAN InsertAtHead,
648     _In_ PCWSTR DriverName,
649     _In_opt_ PCWSTR ImagePath,
650     _In_opt_ PCWSTR GroupName,
651     _In_ ULONG ErrorControl,
652     _In_ ULONG Tag)
653 {
654     PBOOT_DRIVER_NODE DriverNode;
655     PBOOT_DRIVER_LIST_ENTRY DriverEntry;
656     BOOLEAN AlreadyInserted;
657     USHORT PathLength;
658     UNICODE_STRING DriverNameU;
659     UNICODE_STRING RegistryPath;
660     UNICODE_STRING FilePath = {0};
661     UNICODE_STRING RegistryString = {0};
662     UNICODE_STRING GroupString = {0};
663 
664     /* Check whether the driver is already in the list */
665     RtlInitUnicodeString(&DriverNameU, DriverName);
666     AlreadyInserted = CmpIsDriverInList(DriverListHead,
667                                         &DriverNameU,
668                                         &DriverNode);
669     if (AlreadyInserted)
670     {
671         /* If so, we have obtained its node */
672         ASSERT(DriverNode);
673         DriverEntry = &DriverNode->ListEntry;
674     }
675     else
676     {
677         /* Allocate a driver node and initialize it */
678         DriverNode = CmpAllocate(sizeof(BOOT_DRIVER_NODE), FALSE, TAG_CM);
679         if (!DriverNode)
680             return FALSE;
681 
682         RtlZeroMemory(DriverNode, sizeof(BOOT_DRIVER_NODE));
683         DriverEntry = &DriverNode->ListEntry;
684 
685         /* Driver Name */
686         RtlInitEmptyUnicodeString(&DriverNode->Name,
687                                   CmpAllocate(DriverNameU.Length, FALSE, TAG_CM),
688                                   DriverNameU.Length);
689         if (!DriverNode->Name.Buffer)
690             goto Failure;
691 
692         if (!NT_SUCCESS(RtlAppendUnicodeStringToString(&DriverNode->Name, &DriverNameU)))
693             goto Failure;
694     }
695 
696     /* Check whether we have a valid ImagePath. If not, we need
697      * to build it like "System32\\Drivers\\blah.sys" */
698     if (ImagePath && *ImagePath)
699     {
700         /* Just copy ImagePath to the corresponding field in the structure */
701         PathLength = (USHORT)(wcslen(ImagePath)) * sizeof(WCHAR);
702         RtlInitEmptyUnicodeString(&FilePath,
703                                   CmpAllocate(PathLength, FALSE, TAG_WLDR_NAME),
704                                   PathLength);
705         if (!FilePath.Buffer)
706             goto Failure;
707 
708         if (!NT_SUCCESS(RtlAppendUnicodeToString(&FilePath, ImagePath)))
709             goto Failure;
710     }
711     else
712     {
713         /* We have to construct ImagePath ourselves */
714         PathLength = DriverNode->Name.Length + sizeof(L"system32\\drivers\\.sys");
715         RtlInitEmptyUnicodeString(&FilePath,
716                                   CmpAllocate(PathLength, FALSE, TAG_WLDR_NAME),
717                                   PathLength);
718         if (!FilePath.Buffer)
719             goto Failure;
720 
721         if (!NT_SUCCESS(RtlAppendUnicodeToString(&FilePath, L"system32\\drivers\\"))  ||
722             !NT_SUCCESS(RtlAppendUnicodeStringToString(&FilePath, &DriverNode->Name)) ||
723             !NT_SUCCESS(RtlAppendUnicodeToString(&FilePath, L".sys")))
724         {
725             goto Failure;
726         }
727     }
728 
729     /* Registry path */
730     RtlInitUnicodeString(&RegistryPath,
731                          L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
732     PathLength = RegistryPath.Length + DriverNode->Name.Length;
733     RtlInitEmptyUnicodeString(&RegistryString,
734                               CmpAllocate(PathLength, FALSE, TAG_WLDR_NAME),
735                               PathLength);
736     if (!RegistryString.Buffer)
737         goto Failure;
738 
739     if (!NT_SUCCESS(RtlAppendUnicodeStringToString(&RegistryString, &RegistryPath)) ||
740         !NT_SUCCESS(RtlAppendUnicodeStringToString(&RegistryString, &DriverNode->Name)))
741     {
742         goto Failure;
743     }
744 
745     /* Group */
746     if (GroupName && *GroupName)
747     {
748         /*
749          * NOTE: Here we can use our own allocator as we alone maintain the
750          * group string. This is different from the other allocated strings,
751          * where we instead need to use the same (hive) allocator as the
752          * one used by CmpAddDriverToList(), for interoperability purposes.
753          */
754         RtlCreateUnicodeString(&GroupString, GroupName);
755         if (!GroupString.Buffer)
756             goto Failure;
757     }
758     else
759     {
760         RtlInitEmptyUnicodeString(&GroupString, NULL, 0);
761     }
762 
763     /* Set or replace the driver node's file path */
764     if (DriverEntry->FilePath.Buffer)
765     {
766         CmpFree(DriverEntry->FilePath.Buffer,
767                 DriverEntry->FilePath.MaximumLength);
768     }
769     DriverEntry->FilePath = FilePath;
770     FilePath.Buffer = NULL;
771 
772     /* Set or replace the driver node's registry path */
773     if (DriverEntry->RegistryPath.Buffer)
774     {
775         CmpFree(DriverEntry->RegistryPath.Buffer,
776                 DriverEntry->RegistryPath.MaximumLength);
777     }
778     DriverEntry->RegistryPath = RegistryString;
779     RegistryString.Buffer = NULL;
780 
781     /* Set or replace the driver node's group */
782     if (DriverNode->Group.Buffer)
783     {
784         /*
785          * If the buffer is inside the registry hive's memory, this means that
786          * it has been set by CmpAddDriverToList() to point to some data within
787          * the hive; thus we should not free the buffer but just replace it.
788          * Otherwise, this is a buffer previously allocated by ourselves, that
789          * we can free.
790          *
791          * NOTE: This function does not have an explicit LoaderBlock input
792          * parameter pointer, since it does not need it, except for this
793          * very place. So instead, use the global WinLdrSystemBlock pointer.
794          */
795         PLOADER_PARAMETER_BLOCK LoaderBlock =
796             (WinLdrSystemBlock ? &WinLdrSystemBlock->LoaderBlock : NULL);
797 
798         if (!LoaderBlock || !LoaderBlock->RegistryBase || !LoaderBlock->RegistryLength ||
799             ((ULONG_PTR)DriverNode->Group.Buffer <
800                 (ULONG_PTR)VaToPa(LoaderBlock->RegistryBase)) ||
801             ((ULONG_PTR)DriverNode->Group.Buffer >=
802                 (ULONG_PTR)VaToPa(LoaderBlock->RegistryBase) + LoaderBlock->RegistryLength))
803         {
804             RtlFreeUnicodeString(&DriverNode->Group);
805         }
806     }
807     DriverNode->Group = GroupString;
808     GroupString.Buffer = NULL;
809 
810     /* ErrorControl and Tag */
811     DriverNode->ErrorControl = ErrorControl;
812     DriverNode->Tag = Tag;
813 
814     /* Insert the entry into the list if it does not exist there already */
815     if (!AlreadyInserted)
816     {
817         if (InsertAtHead)
818             InsertHeadList(DriverListHead, &DriverEntry->Link);
819         else
820             InsertTailList(DriverListHead, &DriverEntry->Link);
821     }
822 
823     return TRUE;
824 
825 Failure:
826     if (GroupString.Buffer)
827         RtlFreeUnicodeString(&GroupString);
828     if (RegistryString.Buffer)
829         CmpFree(RegistryString.Buffer, RegistryString.MaximumLength);
830     if (FilePath.Buffer)
831         CmpFree(FilePath.Buffer, FilePath.MaximumLength);
832 
833     /* If it does not exist in the list already, free the allocated
834      * driver node, otherwise keep the original one in place. */
835     if (!AlreadyInserted)
836     {
837         if (DriverEntry->RegistryPath.Buffer)
838         {
839             CmpFree(DriverEntry->RegistryPath.Buffer,
840                     DriverEntry->RegistryPath.MaximumLength);
841         }
842         if (DriverEntry->FilePath.Buffer)
843         {
844             CmpFree(DriverEntry->FilePath.Buffer,
845                     DriverEntry->FilePath.MaximumLength);
846         }
847         if (DriverNode->Name.Buffer)
848         {
849             CmpFree(DriverNode->Name.Buffer,
850                     DriverNode->Name.MaximumLength);
851         }
852         CmpFree(DriverNode, sizeof(BOOT_DRIVER_NODE));
853     }
854 
855     return FALSE;
856 }
857