xref: /reactos/ntoskrnl/mm/ARM3/sysldr.c (revision 9cfd8dd9)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         BSD - See COPYING.ARM in the top level directory
4  * FILE:            ntoskrnl/mm/ARM3/sysldr.c
5  * PURPOSE:         Contains the Kernel Loader (SYSLDR) for loading PE files.
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  *                  ReactOS Portable Systems Group
8  */
9 
10 /* INCLUDES *******************************************************************/
11 
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15 
16 #define MODULE_INVOLVED_IN_ARM3
17 #include <mm/ARM3/miarm.h>
18 
19 /* GLOBALS ********************************************************************/
20 
21 LIST_ENTRY PsLoadedModuleList;
22 LIST_ENTRY MmLoadedUserImageList;
23 KSPIN_LOCK PsLoadedModuleSpinLock;
24 ERESOURCE PsLoadedModuleResource;
25 ULONG_PTR PsNtosImageBase;
26 KMUTANT MmSystemLoadLock;
27 
28 PFN_NUMBER MmTotalSystemDriverPages;
29 
30 PVOID MmUnloadedDrivers;
31 PVOID MmLastUnloadedDrivers;
32 
33 BOOLEAN MmMakeLowMemory;
34 BOOLEAN MmEnforceWriteProtection = TRUE;
35 
36 PMMPTE MiKernelResourceStartPte, MiKernelResourceEndPte;
37 ULONG_PTR ExPoolCodeStart, ExPoolCodeEnd, MmPoolCodeStart, MmPoolCodeEnd;
38 ULONG_PTR MmPteCodeStart, MmPteCodeEnd;
39 
40 #ifdef _WIN64
41 #define DEFAULT_SECURITY_COOKIE 0x00002B992DDFA232ll
42 #else
43 #define DEFAULT_SECURITY_COOKIE 0xBB40E64E
44 #endif
45 
46 /* FUNCTIONS ******************************************************************/
47 
48 PVOID
49 NTAPI
50 MiCacheImageSymbols(IN PVOID BaseAddress)
51 {
52     ULONG DebugSize;
53     PVOID DebugDirectory = NULL;
54     PAGED_CODE();
55 
56     /* Make sure it's safe to access the image */
57     _SEH2_TRY
58     {
59         /* Get the debug directory */
60         DebugDirectory = RtlImageDirectoryEntryToData(BaseAddress,
61                                                       TRUE,
62                                                       IMAGE_DIRECTORY_ENTRY_DEBUG,
63                                                       &DebugSize);
64     }
65     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
66     {
67         /* Nothing */
68     }
69     _SEH2_END;
70 
71     /* Return the directory */
72     return DebugDirectory;
73 }
74 
75 NTSTATUS
76 NTAPI
77 MiLoadImageSection(_Inout_ PSECTION *SectionPtr,
78                    _Out_ PVOID *ImageBase,
79                    _In_ PUNICODE_STRING FileName,
80                    _In_ BOOLEAN SessionLoad,
81                    _In_ PLDR_DATA_TABLE_ENTRY LdrEntry)
82 {
83     PSECTION Section = *SectionPtr;
84     NTSTATUS Status;
85     PEPROCESS Process;
86     PVOID Base = NULL;
87     SIZE_T ViewSize = 0;
88     KAPC_STATE ApcState;
89     LARGE_INTEGER SectionOffset = {{0, 0}};
90     BOOLEAN LoadSymbols = FALSE;
91     PFN_COUNT PteCount;
92     PMMPTE PointerPte, LastPte;
93     PVOID DriverBase;
94     MMPTE TempPte;
95     KIRQL OldIrql;
96     PFN_NUMBER PageFrameIndex;
97     PAGED_CODE();
98 
99     /* Detect session load */
100     if (SessionLoad)
101     {
102         /* Fail */
103         UNIMPLEMENTED_DBGBREAK("Session loading not yet supported!\n");
104         return STATUS_NOT_IMPLEMENTED;
105     }
106 
107     /* Not session load, shouldn't have an entry */
108     ASSERT(LdrEntry == NULL);
109 
110     /* Attach to the system process */
111     KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
112 
113     /* Check if we need to load symbols */
114     if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)
115     {
116         /* Yes we do */
117         LoadSymbols = TRUE;
118         NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
119     }
120 
121     /* Map the driver */
122     Process = PsGetCurrentProcess();
123     Status = MmMapViewOfSection(Section,
124                                 Process,
125                                 &Base,
126                                 0,
127                                 0,
128                                 &SectionOffset,
129                                 &ViewSize,
130                                 ViewUnmap,
131                                 0,
132                                 PAGE_EXECUTE);
133 
134     /* Re-enable the flag */
135     if (LoadSymbols) NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
136 
137     /* Check if we failed with distinguished status code */
138     if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
139     {
140         /* Change it to something more generic */
141         Status = STATUS_INVALID_IMAGE_FORMAT;
142     }
143 
144     /* Now check if we failed */
145     if (!NT_SUCCESS(Status))
146     {
147         /* Detach and return */
148         DPRINT1("MmMapViewOfSection failed with status 0x%x\n", Status);
149         KeUnstackDetachProcess(&ApcState);
150         return Status;
151     }
152 
153     /* Reserve system PTEs needed */
154     PteCount = ROUND_TO_PAGES(((PMM_IMAGE_SECTION_OBJECT)Section->Segment)->ImageInformation.ImageFileSize) >> PAGE_SHIFT;
155     PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
156     if (!PointerPte)
157     {
158         DPRINT1("MiReserveSystemPtes failed\n");
159         KeUnstackDetachProcess(&ApcState);
160         return STATUS_INSUFFICIENT_RESOURCES;
161     }
162 
163     /* New driver base */
164     LastPte = PointerPte + PteCount;
165     DriverBase = MiPteToAddress(PointerPte);
166 
167     /* The driver is here */
168     *ImageBase = DriverBase;
169     DPRINT1("Loading: %wZ at %p with %lx pages\n", FileName, DriverBase, PteCount);
170 
171     /* Lock the PFN database */
172     OldIrql = MiAcquirePfnLock();
173 
174     /* Loop the new driver PTEs */
175     TempPte = ValidKernelPte;
176     while (PointerPte < LastPte)
177     {
178         /* Make sure the PTE is not valid for whatever reason */
179         ASSERT(PointerPte->u.Hard.Valid == 0);
180 
181         /* Some debug stuff */
182         MI_SET_USAGE(MI_USAGE_DRIVER_PAGE);
183 #if MI_TRACE_PFNS
184         MI_SET_PROCESS_USTR(FileName);
185 #endif
186 
187         /* Grab a page */
188         PageFrameIndex = MiRemoveAnyPage(MI_GET_NEXT_COLOR());
189 
190         /* Initialize its PFN entry */
191         MiInitializePfn(PageFrameIndex, PointerPte, TRUE);
192 
193         /* Write the PTE */
194         TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
195         MI_WRITE_VALID_PTE(PointerPte, TempPte);
196 
197         /* Move on */
198         PointerPte++;
199     }
200 
201     /* Release the PFN lock */
202     MiReleasePfnLock(OldIrql);
203 
204     /* Copy the image */
205     RtlCopyMemory(DriverBase, Base, PteCount << PAGE_SHIFT);
206 
207     /* Now unmap the view */
208     Status = MmUnmapViewOfSection(Process, Base);
209     ASSERT(NT_SUCCESS(Status));
210 
211     /* Detach and return status */
212     KeUnstackDetachProcess(&ApcState);
213     return Status;
214 }
215 
216 #ifndef RVA
217 #define RVA(m, b) ((PVOID)((ULONG_PTR)(b) + (ULONG_PTR)(m)))
218 #endif
219 
220 USHORT
221 NTAPI
222 NameToOrdinal(
223     _In_ PCSTR ExportName,
224     _In_ PVOID ImageBase,
225     _In_ ULONG NumberOfNames,
226     _In_ PULONG NameTable,
227     _In_ PUSHORT OrdinalTable)
228 {
229     LONG Low, Mid, High, Ret;
230 
231     /* Fail if no names */
232     if (!NumberOfNames)
233         return -1;
234 
235     /* Do a binary search */
236     Low = Mid = 0;
237     High = NumberOfNames - 1;
238     while (High >= Low)
239     {
240         /* Get new middle value */
241         Mid = (Low + High) >> 1;
242 
243         /* Compare name */
244         Ret = strcmp(ExportName, (PCHAR)RVA(ImageBase, NameTable[Mid]));
245         if (Ret < 0)
246         {
247             /* Update high */
248             High = Mid - 1;
249         }
250         else if (Ret > 0)
251         {
252             /* Update low */
253             Low = Mid + 1;
254         }
255         else
256         {
257             /* We got it */
258             break;
259         }
260     }
261 
262     /* Check if we couldn't find it */
263     if (High < Low)
264         return -1;
265 
266     /* Otherwise, this is the ordinal */
267     return OrdinalTable[Mid];
268 }
269 
270 /**
271  * @brief
272  * ReactOS-only helper routine for RtlFindExportedRoutineByName(),
273  * that provides a finer granularity regarding the nature of the
274  * export, and the failure reasons.
275  *
276  * @param[in]   ImageBase
277  * The base address of the loaded image.
278  *
279  * @param[in]   ExportName
280  * The name of the export, given as an ANSI NULL-terminated string.
281  *
282  * @param[out]  Function
283  * The address of the named exported routine, or NULL if not found.
284  * If the export is a forwarder (see @p IsForwarder below), this
285  * address points to the forwarder name.
286  *
287  * @param[out]  IsForwarder
288  * An optional pointer to a BOOLEAN variable, that is set to TRUE
289  * if the found export is a forwarder, and FALSE otherwise.
290  *
291  * @param[in]   NotFoundStatus
292  * The status code to return in case the export could not be found
293  * (examples: STATUS_ENTRYPOINT_NOT_FOUND, STATUS_PROCEDURE_NOT_FOUND).
294  *
295  * @return
296  * A status code as follows:
297  * - STATUS_SUCCESS if the named exported routine is found;
298  * - The custom @p NotFoundStatus if the export could not be found;
299  * - STATUS_INVALID_PARAMETER if the image is invalid or does not
300  *   contain an Export Directory.
301  *
302  * @note
303  * See RtlFindExportedRoutineByName() for more remarks.
304  * Used by psmgr.c PspLookupSystemDllEntryPoint() as well.
305  **/
306 NTSTATUS
307 NTAPI
308 RtlpFindExportedRoutineByName(
309     _In_ PVOID ImageBase,
310     _In_ PCSTR ExportName,
311     _Out_ PVOID* Function,
312     _Out_opt_ PBOOLEAN IsForwarder,
313     _In_ NTSTATUS NotFoundStatus)
314 {
315     PIMAGE_EXPORT_DIRECTORY ExportDirectory;
316     PULONG NameTable;
317     PUSHORT OrdinalTable;
318     ULONG ExportSize;
319     USHORT Ordinal;
320     PULONG ExportTable;
321     ULONG_PTR FunctionAddress;
322 
323     PAGED_CODE();
324 
325     /* Get the export directory */
326     ExportDirectory = RtlImageDirectoryEntryToData(ImageBase,
327                                                    TRUE,
328                                                    IMAGE_DIRECTORY_ENTRY_EXPORT,
329                                                    &ExportSize);
330     if (!ExportDirectory)
331         return STATUS_INVALID_PARAMETER;
332 
333     /* Setup name tables */
334     NameTable = (PULONG)RVA(ImageBase, ExportDirectory->AddressOfNames);
335     OrdinalTable = (PUSHORT)RVA(ImageBase, ExportDirectory->AddressOfNameOrdinals);
336 
337     /* Get the ordinal */
338     Ordinal = NameToOrdinal(ExportName,
339                             ImageBase,
340                             ExportDirectory->NumberOfNames,
341                             NameTable,
342                             OrdinalTable);
343 
344     /* Check if we couldn't find it */
345     if (Ordinal == -1)
346         return NotFoundStatus;
347 
348     /* Validate the ordinal */
349     if (Ordinal >= ExportDirectory->NumberOfFunctions)
350         return NotFoundStatus;
351 
352     /* Resolve the function's address */
353     ExportTable = (PULONG)RVA(ImageBase, ExportDirectory->AddressOfFunctions);
354     FunctionAddress = (ULONG_PTR)RVA(ImageBase, ExportTable[Ordinal]);
355 
356     /* Check if the function is actually a forwarder */
357     if (IsForwarder)
358     {
359         *IsForwarder = FALSE;
360         if ((FunctionAddress > (ULONG_PTR)ExportDirectory) &&
361             (FunctionAddress < (ULONG_PTR)ExportDirectory + ExportSize))
362         {
363             /* It is, and points to the forwarder name */
364             *IsForwarder = TRUE;
365         }
366     }
367 
368     /* We've found it */
369     *Function = (PVOID)FunctionAddress;
370     return STATUS_SUCCESS;
371 }
372 
373 /**
374  * @brief
375  * Finds the address of a given named exported routine in a loaded image.
376  * Note that this function does not support forwarders.
377  *
378  * @param[in]   ImageBase
379  * The base address of the loaded image.
380  *
381  * @param[in]   ExportName
382  * The name of the export, given as an ANSI NULL-terminated string.
383  *
384  * @return
385  * The address of the named exported routine, or NULL if not found.
386  * If the export is a forwarder, this function returns NULL as well.
387  *
388  * @note
389  * This routine was originally named MiLocateExportName(), with a separate
390  * duplicated MiFindExportedRoutineByName() one (taking a PANSI_STRING)
391  * on Windows <= 2003. Both routines have been then merged and renamed
392  * to MiFindExportedRoutineByName() on Windows 8 (taking a PCSTR instead),
393  * and finally renamed and exported as RtlFindExportedRoutineByName() on
394  * Windows 10.
395  *
396  * @see https://www.geoffchappell.com/studies/windows/km/ntoskrnl/api/mm/sysload/mmgetsystemroutineaddress.htm
397  **/
398 PVOID
399 NTAPI
400 RtlFindExportedRoutineByName(
401     _In_ PVOID ImageBase,
402     _In_ PCSTR ExportName)
403 {
404     NTSTATUS Status;
405     BOOLEAN IsForwarder = FALSE;
406     PVOID Function;
407 
408     PAGED_CODE();
409 
410     /* Call the internal API */
411     Status = RtlpFindExportedRoutineByName(ImageBase,
412                                            ExportName,
413                                            &Function,
414                                            &IsForwarder,
415                                            STATUS_ENTRYPOINT_NOT_FOUND);
416     if (!NT_SUCCESS(Status))
417         return NULL;
418 
419     /* If the export is actually a forwarder, log the error and fail */
420     if (IsForwarder)
421     {
422         DPRINT1("RtlFindExportedRoutineByName does not support forwarders!\n", FALSE);
423         return NULL;
424     }
425 
426     /* We've found the export */
427     return Function;
428 }
429 
430 NTSTATUS
431 NTAPI
432 MmCallDllInitialize(
433     _In_ PLDR_DATA_TABLE_ENTRY LdrEntry,
434     _In_ PLIST_ENTRY ModuleListHead)
435 {
436     UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING(
437         L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
438     PMM_DLL_INITIALIZE DllInit;
439     UNICODE_STRING RegPath, ImportName;
440     PCWCH Extension;
441     NTSTATUS Status;
442 
443     PAGED_CODE();
444 
445     /* Try to see if the image exports a DllInitialize routine */
446     DllInit = (PMM_DLL_INITIALIZE)
447         RtlFindExportedRoutineByName(LdrEntry->DllBase, "DllInitialize");
448     if (!DllInit)
449         return STATUS_SUCCESS;
450 
451     /* Make a temporary copy of BaseDllName because we will alter its length */
452     ImportName.Length = LdrEntry->BaseDllName.Length;
453     ImportName.MaximumLength = LdrEntry->BaseDllName.MaximumLength;
454     ImportName.Buffer = LdrEntry->BaseDllName.Buffer;
455 
456     /* Obtain the path to this dll's service in the registry */
457     RegPath.MaximumLength = ServicesKeyName.Length +
458         ImportName.Length + sizeof(UNICODE_NULL);
459     RegPath.Buffer = ExAllocatePoolWithTag(NonPagedPool,
460                                            RegPath.MaximumLength,
461                                            TAG_LDR_WSTR);
462 
463     /* Check if this allocation was unsuccessful */
464     if (!RegPath.Buffer)
465         return STATUS_INSUFFICIENT_RESOURCES;
466 
467     /* Build and append the service name itself */
468     RegPath.Length = ServicesKeyName.Length;
469     RtlCopyMemory(RegPath.Buffer,
470                   ServicesKeyName.Buffer,
471                   ServicesKeyName.Length);
472 
473     /* If the filename has an extension, remove it */
474     Extension = wcschr(ImportName.Buffer, L'.');
475     if (Extension)
476         ImportName.Length = (USHORT)(Extension - ImportName.Buffer) * sizeof(WCHAR);
477 
478     /* Append the service name (base name without extension) */
479     RtlAppendUnicodeStringToString(&RegPath, &ImportName);
480 
481     /* Now call DllInitialize */
482     DPRINT("Calling DllInit(%wZ)\n", &RegPath);
483     Status = DllInit(&RegPath);
484 
485     /* Clean up */
486     ExFreePoolWithTag(RegPath.Buffer, TAG_LDR_WSTR);
487 
488     // TODO: This is for Driver Verifier support.
489     UNREFERENCED_PARAMETER(ModuleListHead);
490 
491     /* Return the DllInitialize status value */
492     return Status;
493 }
494 
495 BOOLEAN
496 MiCallDllUnloadAndUnloadDll(
497     _In_ PLDR_DATA_TABLE_ENTRY LdrEntry)
498 {
499     NTSTATUS Status;
500     PMM_DLL_UNLOAD DllUnload;
501 
502     PAGED_CODE();
503 
504     /* Retrieve the DllUnload routine */
505     DllUnload = (PMM_DLL_UNLOAD)
506         RtlFindExportedRoutineByName(LdrEntry->DllBase, "DllUnload");
507     if (!DllUnload)
508         return FALSE;
509 
510     /* Call it and check for success */
511     Status = DllUnload();
512     if (!NT_SUCCESS(Status))
513         return FALSE;
514 
515     /* Lie about the load count so we can unload the image */
516     ASSERT(LdrEntry->LoadCount == 0);
517     LdrEntry->LoadCount = 1;
518 
519     /* Unload it */
520     MmUnloadSystemImage(LdrEntry);
521     return TRUE;
522 }
523 
524 NTSTATUS
525 NTAPI
526 MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
527 {
528     SIZE_T i;
529     LOAD_IMPORTS SingleEntry;
530     PLDR_DATA_TABLE_ENTRY LdrEntry;
531     PVOID CurrentImports;
532     PAGED_CODE();
533 
534     /* Check if there's no imports or if we're a boot driver */
535     if ((ImportList == MM_SYSLDR_NO_IMPORTS) ||
536         (ImportList == MM_SYSLDR_BOOT_LOADED) ||
537         (ImportList->Count == 0))
538     {
539         /* Then there's nothing to do */
540         return STATUS_SUCCESS;
541     }
542 
543     /* Check for single-entry */
544     if ((ULONG_PTR)ImportList & MM_SYSLDR_SINGLE_ENTRY)
545     {
546         /* Set it up */
547         SingleEntry.Count = 1;
548         SingleEntry.Entry[0] = (PVOID)((ULONG_PTR)ImportList &~ MM_SYSLDR_SINGLE_ENTRY);
549 
550         /* Use this as the import list */
551         ImportList = &SingleEntry;
552     }
553 
554     /* Loop the import list */
555     for (i = 0; (i < ImportList->Count) && (ImportList->Entry[i]); i++)
556     {
557         /* Get the entry */
558         LdrEntry = ImportList->Entry[i];
559         DPRINT1("%wZ <%wZ>\n", &LdrEntry->FullDllName, &LdrEntry->BaseDllName);
560 
561         /* Skip boot loaded images */
562         if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) continue;
563 
564         /* Dereference the entry */
565         ASSERT(LdrEntry->LoadCount >= 1);
566         if (!--LdrEntry->LoadCount)
567         {
568             /* Save the import data in case unload fails */
569             CurrentImports = LdrEntry->LoadedImports;
570 
571             /* This is the last entry */
572             LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
573             if (MiCallDllUnloadAndUnloadDll(LdrEntry))
574             {
575                 /* Unloading worked, parse this DLL's imports too */
576                 MiDereferenceImports(CurrentImports);
577 
578                 /* Check if we had valid imports */
579                 if ((CurrentImports != MM_SYSLDR_BOOT_LOADED) &&
580                     (CurrentImports != MM_SYSLDR_NO_IMPORTS) &&
581                     !((ULONG_PTR)CurrentImports & MM_SYSLDR_SINGLE_ENTRY))
582                 {
583                     /* Free them */
584                     ExFreePoolWithTag(CurrentImports, TAG_LDR_IMPORTS);
585                 }
586             }
587             else
588             {
589                 /* Unload failed, restore imports */
590                 LdrEntry->LoadedImports = CurrentImports;
591             }
592         }
593     }
594 
595     /* Done */
596     return STATUS_SUCCESS;
597 }
598 
599 VOID
600 NTAPI
601 MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
602 {
603     PAGED_CODE();
604 
605     /* Check if there's no imports or we're a boot driver or only one entry */
606     if ((LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) ||
607         (LdrEntry->LoadedImports == MM_SYSLDR_NO_IMPORTS) ||
608         ((ULONG_PTR)LdrEntry->LoadedImports & MM_SYSLDR_SINGLE_ENTRY))
609     {
610         /* Nothing to do */
611         return;
612     }
613 
614     /* Otherwise, free the import list */
615     ExFreePoolWithTag(LdrEntry->LoadedImports, TAG_LDR_IMPORTS);
616     LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
617 }
618 
619 VOID
620 NTAPI
621 MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
622                      IN BOOLEAN Insert)
623 {
624     KIRQL OldIrql;
625 
626     /* Acquire module list lock */
627     KeEnterCriticalRegion();
628     ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
629 
630     /* Acquire the spinlock too as we will insert or remove the entry */
631     OldIrql = KeAcquireSpinLockRaiseToSynch(&PsLoadedModuleSpinLock);
632 
633     /* Insert or remove from the list */
634     if (Insert)
635         InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks);
636     else
637         RemoveEntryList(&LdrEntry->InLoadOrderLinks);
638 
639     /* Release locks */
640     KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
641     ExReleaseResourceLite(&PsLoadedModuleResource);
642     KeLeaveCriticalRegion();
643 }
644 
645 CODE_SEG("INIT")
646 VOID
647 NTAPI
648 MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
649                IN PVOID OldBase,
650                IN PVOID NewBase,
651                IN ULONG Size)
652 {
653     ULONG_PTR OldBaseTop, Delta;
654     PLDR_DATA_TABLE_ENTRY LdrEntry;
655     PLIST_ENTRY NextEntry;
656     ULONG ImportSize;
657     //
658     // FIXME: MINGW-W64 must fix LD to generate drivers that Windows can load,
659     // since a real version of Windows would fail at this point, but they seem
660     // busy implementing features such as "HotPatch" support in GCC 4.6 instead,
661     // a feature which isn't even used by Windows. Priorities, priorities...
662     // Please note that Microsoft WDK EULA and license prohibits using
663     // the information contained within it for the generation of "non-Windows"
664     // drivers, which is precisely what LD will generate, since an LD driver
665     // will not load on Windows.
666     //
667 #ifdef _WORKING_LINKER_
668     ULONG i;
669 #endif
670     PULONG_PTR ImageThunk;
671     PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
672 
673     /* Calculate the top and delta */
674     OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
675     Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
676 
677     /* Loop the loader block */
678     for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
679          NextEntry != &LoaderBlock->LoadOrderListHead;
680          NextEntry = NextEntry->Flink)
681     {
682         /* Get the loader entry */
683         LdrEntry = CONTAINING_RECORD(NextEntry,
684                                      LDR_DATA_TABLE_ENTRY,
685                                      InLoadOrderLinks);
686 #ifdef _WORKING_LINKER_
687         /* Get the IAT */
688         ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
689                                                   TRUE,
690                                                   IMAGE_DIRECTORY_ENTRY_IAT,
691                                                   &ImportSize);
692         if (!ImageThunk) continue;
693 
694         /* Make sure we have an IAT */
695         DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
696         for (i = 0; i < ImportSize; i++, ImageThunk++)
697         {
698             /* Check if it's within this module */
699             if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
700             {
701                 /* Relocate it */
702                 DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
703                         ImageThunk, *ImageThunk, *ImageThunk + Delta);
704                 *ImageThunk += Delta;
705             }
706         }
707 #else
708         /* Get the import table */
709         ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
710                                                         TRUE,
711                                                         IMAGE_DIRECTORY_ENTRY_IMPORT,
712                                                         &ImportSize);
713         if (!ImportDescriptor) continue;
714 
715         /* Make sure we have an IAT */
716         DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
717         while ((ImportDescriptor->Name) &&
718                (ImportDescriptor->OriginalFirstThunk))
719         {
720             /* Get the image thunk */
721             ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
722                                  ImportDescriptor->FirstThunk);
723             while (*ImageThunk)
724             {
725                 /* Check if it's within this module */
726                 if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
727                 {
728                     /* Relocate it */
729                     DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
730                             ImageThunk, *ImageThunk, *ImageThunk + Delta);
731                     *ImageThunk += Delta;
732                 }
733 
734                 /* Go to the next thunk */
735                 ImageThunk++;
736             }
737 
738             /* Go to the next import */
739             ImportDescriptor++;
740         }
741 #endif
742     }
743 }
744 
745 NTSTATUS
746 NTAPI
747 MiSnapThunk(IN PVOID DllBase,
748             IN PVOID ImageBase,
749             IN PIMAGE_THUNK_DATA Name,
750             IN PIMAGE_THUNK_DATA Address,
751             IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
752             IN ULONG ExportSize,
753             IN BOOLEAN SnapForwarder,
754             OUT PCHAR *MissingApi)
755 {
756     BOOLEAN IsOrdinal;
757     USHORT Ordinal;
758     PULONG NameTable;
759     PUSHORT OrdinalTable;
760     PIMAGE_IMPORT_BY_NAME NameImport;
761     USHORT Hint;
762     NTSTATUS Status;
763     PCHAR MissingForwarder;
764     CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
765     PULONG ExportTable;
766     ANSI_STRING DllName;
767     UNICODE_STRING ForwarderName;
768     PLIST_ENTRY NextEntry;
769     PLDR_DATA_TABLE_ENTRY LdrEntry;
770     ULONG ForwardExportSize;
771     PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
772     PIMAGE_IMPORT_BY_NAME ForwardName;
773     SIZE_T ForwardLength;
774     IMAGE_THUNK_DATA ForwardThunk;
775 
776     PAGED_CODE();
777 
778     /* Check if this is an ordinal */
779     IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
780     if ((IsOrdinal) && !(SnapForwarder))
781     {
782         /* Get the ordinal number and set it as missing */
783         Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
784                            ExportDirectory->Base);
785         *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
786     }
787     else
788     {
789         /* Get the VA if we don't have to snap */
790         if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
791         NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
792 
793         /* Copy the procedure name */
794         RtlStringCbCopyA(*MissingApi,
795                          MAXIMUM_FILENAME_LENGTH,
796                          (PCHAR)NameImport->Name);
797 
798         /* Setup name tables */
799         DPRINT("Import name: %s\n", NameImport->Name);
800         NameTable = (PULONG)((ULONG_PTR)DllBase +
801                              ExportDirectory->AddressOfNames);
802         OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
803                                  ExportDirectory->AddressOfNameOrdinals);
804 
805         /* Get the hint and check if it's valid */
806         Hint = NameImport->Hint;
807         if ((Hint < ExportDirectory->NumberOfNames) &&
808             !(strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
809         {
810             /* We have a match, get the ordinal number from here */
811             Ordinal = OrdinalTable[Hint];
812         }
813         else
814         {
815             /* Do a binary search */
816             Ordinal = NameToOrdinal((PCHAR)NameImport->Name,
817                                     DllBase,
818                                     ExportDirectory->NumberOfNames,
819                                     NameTable,
820                                     OrdinalTable);
821 
822             /* Check if we couldn't find it */
823             if (Ordinal == -1)
824             {
825                 DPRINT1("Warning: Driver failed to load, %s not found\n", NameImport->Name);
826                 return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
827             }
828         }
829     }
830 
831     /* Check if the ordinal is valid */
832     if (Ordinal >= ExportDirectory->NumberOfFunctions)
833     {
834         /* It's not, fail */
835         Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
836     }
837     else
838     {
839         /* In case the forwarder is missing */
840         MissingForwarder = NameBuffer;
841 
842         /* Resolve the address and write it */
843         ExportTable = (PULONG)((ULONG_PTR)DllBase +
844                                ExportDirectory->AddressOfFunctions);
845         Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
846 
847         /* Assume success from now on */
848         Status = STATUS_SUCCESS;
849 
850         /* Check if the function is actually a forwarder */
851         if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
852             (Address->u1.Function < (ULONG_PTR)ExportDirectory + ExportSize))
853         {
854             /* Now assume failure in case the forwarder doesn't exist */
855             Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
856 
857             /* Build the forwarder name */
858             DllName.Buffer = (PCHAR)Address->u1.Function;
859             DllName.Length = (USHORT)(strchr(DllName.Buffer, '.') -
860                                       DllName.Buffer) +
861                                       sizeof(ANSI_NULL);
862             DllName.MaximumLength = DllName.Length;
863 
864             /* Convert it */
865             if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
866                                                          &DllName,
867                                                          TRUE)))
868             {
869                 /* We failed, just return an error */
870                 return Status;
871             }
872 
873             /* Loop the module list */
874             NextEntry = PsLoadedModuleList.Flink;
875             while (NextEntry != &PsLoadedModuleList)
876             {
877                 /* Get the loader entry */
878                 LdrEntry = CONTAINING_RECORD(NextEntry,
879                                              LDR_DATA_TABLE_ENTRY,
880                                              InLoadOrderLinks);
881 
882                 /* Check if it matches */
883                 if (RtlPrefixUnicodeString(&ForwarderName,
884                                            &LdrEntry->BaseDllName,
885                                            TRUE))
886                 {
887                     /* Get the forwarder export directory */
888                     ForwardExportDirectory =
889                         RtlImageDirectoryEntryToData(LdrEntry->DllBase,
890                                                      TRUE,
891                                                      IMAGE_DIRECTORY_ENTRY_EXPORT,
892                                                      &ForwardExportSize);
893                     if (!ForwardExportDirectory) break;
894 
895                     /* Allocate a name entry */
896                     ForwardLength = strlen(DllName.Buffer + DllName.Length) +
897                                     sizeof(ANSI_NULL);
898                     ForwardName = ExAllocatePoolWithTag(PagedPool,
899                                                         sizeof(*ForwardName) +
900                                                         ForwardLength,
901                                                         TAG_LDR_WSTR);
902                     if (!ForwardName) break;
903 
904                     /* Copy the data */
905                     RtlCopyMemory(&ForwardName->Name[0],
906                                   DllName.Buffer + DllName.Length,
907                                   ForwardLength);
908                     ForwardName->Hint = 0;
909 
910                     /* Set the new address */
911                     ForwardThunk.u1.AddressOfData = (ULONG_PTR)ForwardName;
912 
913                     /* Snap the forwarder */
914                     Status = MiSnapThunk(LdrEntry->DllBase,
915                                          ImageBase,
916                                          &ForwardThunk,
917                                          &ForwardThunk,
918                                          ForwardExportDirectory,
919                                          ForwardExportSize,
920                                          TRUE,
921                                          &MissingForwarder);
922 
923                     /* Free the forwarder name and set the thunk */
924                     ExFreePoolWithTag(ForwardName, TAG_LDR_WSTR);
925                     Address->u1 = ForwardThunk.u1;
926                     break;
927                 }
928 
929                 /* Go to the next entry */
930                 NextEntry = NextEntry->Flink;
931             }
932 
933             /* Free the name */
934             RtlFreeUnicodeString(&ForwarderName);
935         }
936     }
937 
938     /* Return status */
939     return Status;
940 }
941 
942 NTSTATUS
943 NTAPI
944 MmUnloadSystemImage(IN PVOID ImageHandle)
945 {
946     PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
947     PVOID BaseAddress = LdrEntry->DllBase;
948     NTSTATUS Status;
949     STRING TempName;
950     BOOLEAN HadEntry = FALSE;
951 
952     /* Acquire the loader lock */
953     KeEnterCriticalRegion();
954     KeWaitForSingleObject(&MmSystemLoadLock,
955                           WrVirtualMemory,
956                           KernelMode,
957                           FALSE,
958                           NULL);
959 
960     /* Check if this driver was loaded at boot and didn't get imports parsed */
961     if (LdrEntry->LoadedImports == MM_SYSLDR_BOOT_LOADED) goto Done;
962 
963     /* We should still be alive */
964     ASSERT(LdrEntry->LoadCount != 0);
965     LdrEntry->LoadCount--;
966 
967     /* Check if we're still loaded */
968     if (LdrEntry->LoadCount) goto Done;
969 
970     /* We should cleanup... are symbols loaded */
971     if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
972     {
973         /* Create the ANSI name */
974         Status = RtlUnicodeStringToAnsiString(&TempName,
975                                               &LdrEntry->BaseDllName,
976                                               TRUE);
977         if (NT_SUCCESS(Status))
978         {
979             /* Unload the symbols */
980             DbgUnLoadImageSymbols(&TempName,
981                                   BaseAddress,
982                                   (ULONG_PTR)PsGetCurrentProcessId());
983             RtlFreeAnsiString(&TempName);
984         }
985     }
986 
987     /* FIXME: Free the driver */
988     DPRINT1("Leaking driver: %wZ\n", &LdrEntry->BaseDllName);
989     //MmFreeSection(LdrEntry->DllBase);
990 
991     /* Check if we're linked in */
992     if (LdrEntry->InLoadOrderLinks.Flink)
993     {
994         /* Remove us */
995         MiProcessLoaderEntry(LdrEntry, FALSE);
996         HadEntry = TRUE;
997     }
998 
999     /* Dereference and clear the imports */
1000     MiDereferenceImports(LdrEntry->LoadedImports);
1001     MiClearImports(LdrEntry);
1002 
1003     /* Check if the entry needs to go away */
1004     if (HadEntry)
1005     {
1006         /* Check if it had a name */
1007         if (LdrEntry->FullDllName.Buffer)
1008         {
1009             /* Free it */
1010             ExFreePoolWithTag(LdrEntry->FullDllName.Buffer, TAG_LDR_WSTR);
1011         }
1012 
1013         /* Check if we had a section */
1014         if (LdrEntry->SectionPointer)
1015         {
1016             /* Dereference it */
1017             ObDereferenceObject(LdrEntry->SectionPointer);
1018         }
1019 
1020         /* Free the entry */
1021         ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
1022     }
1023 
1024     /* Release the system lock and return */
1025 Done:
1026     KeReleaseMutant(&MmSystemLoadLock, MUTANT_INCREMENT, FALSE, FALSE);
1027     KeLeaveCriticalRegion();
1028     return STATUS_SUCCESS;
1029 }
1030 
1031 NTSTATUS
1032 NTAPI
1033 MiResolveImageReferences(IN PVOID ImageBase,
1034                          IN PUNICODE_STRING ImageFileDirectory,
1035                          IN PUNICODE_STRING NamePrefix OPTIONAL,
1036                          OUT PCHAR *MissingApi,
1037                          OUT PWCHAR *MissingDriver,
1038                          OUT PLOAD_IMPORTS *LoadImports)
1039 {
1040     static UNICODE_STRING DriversFolderName = RTL_CONSTANT_STRING(L"drivers\\");
1041     PCHAR MissingApiBuffer = *MissingApi, ImportName;
1042     PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
1043     ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
1044     PLOAD_IMPORTS LoadedImports, NewImports;
1045     ULONG i;
1046     BOOLEAN GdiLink, NormalLink;
1047     BOOLEAN ReferenceNeeded, Loaded;
1048     ANSI_STRING TempString;
1049     UNICODE_STRING NameString, DllName;
1050     PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
1051     PVOID ImportBase, DllBase;
1052     PLIST_ENTRY NextEntry;
1053     PIMAGE_EXPORT_DIRECTORY ExportDirectory;
1054     NTSTATUS Status;
1055     PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
1056 
1057     PAGED_CODE();
1058 
1059     DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
1060            __FUNCTION__, ImageBase, ImageFileDirectory);
1061 
1062     /* No name string buffer yet */
1063     NameString.Buffer = NULL;
1064 
1065     /* Assume no imports */
1066     *LoadImports = MM_SYSLDR_NO_IMPORTS;
1067 
1068     /* Get the import descriptor */
1069     ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
1070                                                     TRUE,
1071                                                     IMAGE_DIRECTORY_ENTRY_IMPORT,
1072                                                     &ImportSize);
1073     if (!ImportDescriptor) return STATUS_SUCCESS;
1074 
1075     /* Loop all imports to count them */
1076     for (CurrentImport = ImportDescriptor;
1077          (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
1078          CurrentImport++)
1079     {
1080         /* One more */
1081         ImportCount++;
1082     }
1083 
1084     /* Make sure we have non-zero imports */
1085     if (ImportCount)
1086     {
1087         /* Calculate and allocate the list we'll need */
1088         LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
1089         LoadedImports = ExAllocatePoolWithTag(PagedPool,
1090                                               LoadedImportsSize,
1091                                               TAG_LDR_IMPORTS);
1092         if (LoadedImports)
1093         {
1094             /* Zero it */
1095             RtlZeroMemory(LoadedImports, LoadedImportsSize);
1096             LoadedImports->Count = ImportCount;
1097         }
1098     }
1099     else
1100     {
1101         /* No table */
1102         LoadedImports = NULL;
1103     }
1104 
1105     /* Reset the import count and loop descriptors again */
1106     GdiLink = NormalLink = FALSE;
1107     ImportCount = 0;
1108     while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
1109     {
1110         /* Get the name */
1111         ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
1112 
1113         /* Check if this is a GDI driver */
1114         GdiLink = GdiLink ||
1115                   !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
1116 
1117         /* We can also allow dxapi (for Windows compat, allow IRT and coverage) */
1118         NormalLink = NormalLink ||
1119                      ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
1120                       (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)) &&
1121                       (_strnicmp(ImportName, "coverage", sizeof("coverage") - 1)) &&
1122                       (_strnicmp(ImportName, "irt", sizeof("irt") - 1)));
1123 
1124         /* Check if this is a valid GDI driver */
1125         if (GdiLink && NormalLink)
1126         {
1127             /* It's not, it's importing stuff it shouldn't be! */
1128             Status = STATUS_PROCEDURE_NOT_FOUND;
1129             goto Failure;
1130         }
1131 
1132         /* Check for user-mode printer or video card drivers, which don't belong */
1133         if (!(_strnicmp(ImportName, "ntdll", sizeof("ntdll") - 1)) ||
1134             !(_strnicmp(ImportName, "winsrv", sizeof("winsrv") - 1)) ||
1135             !(_strnicmp(ImportName, "advapi32", sizeof("advapi32") - 1)) ||
1136             !(_strnicmp(ImportName, "kernel32", sizeof("kernel32") - 1)) ||
1137             !(_strnicmp(ImportName, "user32", sizeof("user32") - 1)) ||
1138             !(_strnicmp(ImportName, "gdi32", sizeof("gdi32") - 1)))
1139         {
1140             /* This is not kernel code */
1141             Status = STATUS_PROCEDURE_NOT_FOUND;
1142             goto Failure;
1143         }
1144 
1145         /* Check if this is a "core" import, which doesn't get referenced */
1146         if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
1147             !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
1148             !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
1149         {
1150             /* Don't reference this */
1151             ReferenceNeeded = FALSE;
1152         }
1153         else
1154         {
1155             /* Reference these modules */
1156             ReferenceNeeded = TRUE;
1157         }
1158 
1159         /* Now setup a unicode string for the import */
1160         RtlInitAnsiString(&TempString, ImportName);
1161         Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
1162         if (!NT_SUCCESS(Status))
1163         {
1164             /* Failed */
1165             goto Failure;
1166         }
1167 
1168         /* We don't support name prefixes yet */
1169         if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
1170 
1171         /* Remember that we haven't loaded the import at this point */
1172 CheckDllState:
1173         Loaded = FALSE;
1174         ImportBase = NULL;
1175 
1176         /* Loop the driver list */
1177         NextEntry = PsLoadedModuleList.Flink;
1178         while (NextEntry != &PsLoadedModuleList)
1179         {
1180             /* Get the loader entry and compare the name */
1181             LdrEntry = CONTAINING_RECORD(NextEntry,
1182                                          LDR_DATA_TABLE_ENTRY,
1183                                          InLoadOrderLinks);
1184             if (RtlEqualUnicodeString(&NameString,
1185                                       &LdrEntry->BaseDllName,
1186                                       TRUE))
1187             {
1188                 /* Get the base address */
1189                 ImportBase = LdrEntry->DllBase;
1190 
1191                 /* Check if we haven't loaded yet, and we need references */
1192                 if (!(Loaded) && (ReferenceNeeded))
1193                 {
1194                     /* Make sure we're not already loading */
1195                     if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
1196                     {
1197                         /* Increase the load count */
1198                         LdrEntry->LoadCount++;
1199                     }
1200                 }
1201 
1202                 /* Done, break out */
1203                 break;
1204             }
1205 
1206             /* Go to the next entry */
1207             NextEntry = NextEntry->Flink;
1208         }
1209 
1210         /* Check if we haven't loaded the import yet */
1211         if (!ImportBase)
1212         {
1213             /* Setup the import DLL name */
1214             DllName.MaximumLength = NameString.Length +
1215                                     ImageFileDirectory->Length +
1216                                     sizeof(UNICODE_NULL);
1217             DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
1218                                                    DllName.MaximumLength,
1219                                                    TAG_LDR_WSTR);
1220             if (!DllName.Buffer)
1221             {
1222                 /* We're out of resources */
1223                 Status = STATUS_INSUFFICIENT_RESOURCES;
1224                 goto Failure;
1225             }
1226 
1227             /* Add the import name to the base directory */
1228             RtlCopyUnicodeString(&DllName, ImageFileDirectory);
1229             RtlAppendUnicodeStringToString(&DllName,
1230                                            &NameString);
1231 
1232             /* Load the image */
1233             Status = MmLoadSystemImage(&DllName,
1234                                        NamePrefix,
1235                                        NULL,
1236                                        FALSE,
1237                                        (PVOID *)&DllEntry,
1238                                        &DllBase);
1239 
1240             /* win32k / GDI drivers can also import from system32 folder */
1241             if ((Status == STATUS_OBJECT_NAME_NOT_FOUND) &&
1242                 (MI_IS_SESSION_ADDRESS(ImageBase) || 1)) // HACK
1243             {
1244                 /* Free the old name buffer */
1245                 ExFreePoolWithTag(DllName.Buffer, TAG_LDR_WSTR);
1246 
1247                 /* Calculate size for a string the adds 'drivers\' */
1248                 DllName.MaximumLength += DriversFolderName.Length;
1249 
1250                 /* Allocate the new buffer */
1251                 DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
1252                                                        DllName.MaximumLength,
1253                                                        TAG_LDR_WSTR);
1254                 if (!DllName.Buffer)
1255                 {
1256                     /* We're out of resources */
1257                     Status = STATUS_INSUFFICIENT_RESOURCES;
1258                     goto Failure;
1259                 }
1260 
1261                 /* Copy image directory and append 'drivers\' folder name */
1262                 RtlCopyUnicodeString(&DllName, ImageFileDirectory);
1263                 RtlAppendUnicodeStringToString(&DllName, &DriversFolderName);
1264 
1265                 /* Now add the import name */
1266                 RtlAppendUnicodeStringToString(&DllName, &NameString);
1267 
1268                 /* Try once again to load the image */
1269                 Status = MmLoadSystemImage(&DllName,
1270                                            NamePrefix,
1271                                            NULL,
1272                                            FALSE,
1273                                            (PVOID *)&DllEntry,
1274                                            &DllBase);
1275             }
1276 
1277             if (!NT_SUCCESS(Status))
1278             {
1279                 /* Fill out the information for the error */
1280                 *MissingDriver = DllName.Buffer;
1281                 *(PULONG)MissingDriver |= 1;
1282                 *MissingApi = NULL;
1283 
1284                 DPRINT1("Failed to load dependency: %wZ\n", &DllName);
1285 
1286                 /* Don't free the name */
1287                 DllName.Buffer = NULL;
1288 
1289                 /* Cleanup and return */
1290                 goto Failure;
1291             }
1292 
1293             /* We can free the DLL Name */
1294             ExFreePoolWithTag(DllName.Buffer, TAG_LDR_WSTR);
1295             DllName.Buffer = NULL;
1296 
1297             /* We're now loaded */
1298             Loaded = TRUE;
1299 
1300             /* Sanity check */
1301             ASSERT(DllBase == DllEntry->DllBase);
1302 
1303             /* Call the initialization routines */
1304             Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
1305             if (!NT_SUCCESS(Status))
1306             {
1307                 /* We failed, unload the image */
1308                 MmUnloadSystemImage(DllEntry);
1309                 ERROR_DBGBREAK("MmCallDllInitialize failed with status 0x%x\n", Status);
1310                 Loaded = FALSE;
1311             }
1312 
1313             /* Loop again to make sure that everything is OK */
1314             goto CheckDllState;
1315         }
1316 
1317         /* Check if we're support to reference this import */
1318         if ((ReferenceNeeded) && (LoadedImports))
1319         {
1320             /* Make sure we're not already loading */
1321             if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
1322             {
1323                 /* Add the entry */
1324                 LoadedImports->Entry[ImportCount] = LdrEntry;
1325                 ImportCount++;
1326             }
1327         }
1328 
1329         /* Free the import name */
1330         RtlFreeUnicodeString(&NameString);
1331 
1332         /* Set the missing driver name and get the export directory */
1333         *MissingDriver = LdrEntry->BaseDllName.Buffer;
1334         ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
1335                                                        TRUE,
1336                                                        IMAGE_DIRECTORY_ENTRY_EXPORT,
1337                                                        &ExportSize);
1338         if (!ExportDirectory)
1339         {
1340             /* Cleanup and return */
1341             DPRINT1("Warning: Driver failed to load, %S not found\n", *MissingDriver);
1342             Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
1343             goto Failure;
1344         }
1345 
1346         /* Make sure we have an IAT */
1347         if (ImportDescriptor->OriginalFirstThunk)
1348         {
1349             /* Get the first thunks */
1350             OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
1351                                 ImportDescriptor->OriginalFirstThunk);
1352             FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
1353                                  ImportDescriptor->FirstThunk);
1354 
1355             /* Loop the IAT */
1356             while (OrigThunk->u1.AddressOfData)
1357             {
1358                 /* Snap thunk */
1359                 Status = MiSnapThunk(ImportBase,
1360                                      ImageBase,
1361                                      OrigThunk++,
1362                                      FirstThunk++,
1363                                      ExportDirectory,
1364                                      ExportSize,
1365                                      FALSE,
1366                                      MissingApi);
1367                 if (!NT_SUCCESS(Status))
1368                 {
1369                     /* Cleanup and return */
1370                     goto Failure;
1371                 }
1372 
1373                 /* Reset the buffer */
1374                 *MissingApi = MissingApiBuffer;
1375             }
1376         }
1377 
1378         /* Go to the next import */
1379         ImportDescriptor++;
1380     }
1381 
1382     /* Check if we have an import list */
1383     if (LoadedImports)
1384     {
1385         /* Reset the count again, and loop entries */
1386         ImportCount = 0;
1387         for (i = 0; i < LoadedImports->Count; i++)
1388         {
1389             if (LoadedImports->Entry[i])
1390             {
1391                 /* Got an entry, OR it with 1 in case it's the single entry */
1392                 ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] |
1393                                       MM_SYSLDR_SINGLE_ENTRY);
1394                 ImportCount++;
1395             }
1396         }
1397 
1398         /* Check if we had no imports */
1399         if (!ImportCount)
1400         {
1401             /* Free the list and set it to no imports */
1402             ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
1403             LoadedImports = MM_SYSLDR_NO_IMPORTS;
1404         }
1405         else if (ImportCount == 1)
1406         {
1407             /* Just one entry, we can free the table and only use our entry */
1408             ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
1409             LoadedImports = (PLOAD_IMPORTS)ImportEntry;
1410         }
1411         else if (ImportCount != LoadedImports->Count)
1412         {
1413             /* Allocate a new list */
1414             LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
1415             NewImports = ExAllocatePoolWithTag(PagedPool,
1416                                                LoadedImportsSize,
1417                                                TAG_LDR_IMPORTS);
1418             if (NewImports)
1419             {
1420                 /* Set count */
1421                 NewImports->Count = 0;
1422 
1423                 /* Loop all the imports */
1424                 for (i = 0; i < LoadedImports->Count; i++)
1425                 {
1426                     /* Make sure it's valid */
1427                     if (LoadedImports->Entry[i])
1428                     {
1429                         /* Copy it */
1430                         NewImports->Entry[NewImports->Count] = LoadedImports->Entry[i];
1431                         NewImports->Count++;
1432                     }
1433                 }
1434 
1435                 /* Free the old copy */
1436                 ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
1437                 LoadedImports = NewImports;
1438             }
1439         }
1440 
1441         /* Return the list */
1442         *LoadImports = LoadedImports;
1443     }
1444 
1445     /* Return success */
1446     return STATUS_SUCCESS;
1447 
1448 Failure:
1449 
1450     /* Cleanup and return */
1451     RtlFreeUnicodeString(&NameString);
1452 
1453     if (LoadedImports)
1454     {
1455         MiDereferenceImports(LoadedImports);
1456         ExFreePoolWithTag(LoadedImports, TAG_LDR_IMPORTS);
1457     }
1458 
1459     return Status;
1460 }
1461 
1462 VOID
1463 NTAPI
1464 MiFreeInitializationCode(IN PVOID InitStart,
1465                          IN PVOID InitEnd)
1466 {
1467     PMMPTE PointerPte;
1468     PFN_NUMBER PagesFreed;
1469 
1470     /* Get the start PTE */
1471     PointerPte = MiAddressToPte(InitStart);
1472     ASSERT(MI_IS_PHYSICAL_ADDRESS(InitStart) == FALSE);
1473 
1474     /*  Compute the number of pages we expect to free */
1475     PagesFreed = (PFN_NUMBER)(MiAddressToPte(InitEnd) - PointerPte);
1476 
1477     /* Try to actually free them */
1478     PagesFreed = MiDeleteSystemPageableVm(PointerPte,
1479                                           PagesFreed,
1480                                           0,
1481                                           NULL);
1482 }
1483 
1484 CODE_SEG("INIT")
1485 VOID
1486 NTAPI
1487 MiFindInitializationCode(OUT PVOID *StartVa,
1488                          OUT PVOID *EndVa)
1489 {
1490     ULONG Size, SectionCount, Alignment;
1491     PLDR_DATA_TABLE_ENTRY LdrEntry;
1492     ULONG_PTR DllBase, InitStart, InitEnd, ImageEnd, InitCode;
1493     PLIST_ENTRY NextEntry;
1494     PIMAGE_NT_HEADERS NtHeader;
1495     PIMAGE_SECTION_HEADER Section, LastSection, InitSection;
1496     BOOLEAN InitFound;
1497     DBG_UNREFERENCED_LOCAL_VARIABLE(InitSection);
1498 
1499     /* So we don't free our own code yet */
1500     InitCode = (ULONG_PTR)&MiFindInitializationCode;
1501 
1502     /* Assume failure */
1503     *StartVa = NULL;
1504 
1505     /* Acquire the necessary locks while we loop the list */
1506     KeEnterCriticalRegion();
1507     KeWaitForSingleObject(&MmSystemLoadLock,
1508                           WrVirtualMemory,
1509                           KernelMode,
1510                           FALSE,
1511                           NULL);
1512     ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
1513 
1514     /* Loop all loaded modules */
1515     NextEntry = PsLoadedModuleList.Flink;
1516     while (NextEntry != &PsLoadedModuleList)
1517     {
1518         /* Get the loader entry and its DLL base */
1519         LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1520         DllBase = (ULONG_PTR)LdrEntry->DllBase;
1521 
1522         /* Only process boot loaded images. Other drivers are processed by
1523            MmFreeDriverInitialization */
1524         if (LdrEntry->Flags & LDRP_MM_LOADED)
1525         {
1526             /* Keep going */
1527             NextEntry = NextEntry->Flink;
1528             continue;
1529         }
1530 
1531         /* Get the NT header */
1532         NtHeader = RtlImageNtHeader((PVOID)DllBase);
1533         if (!NtHeader)
1534         {
1535             /* Keep going */
1536             NextEntry = NextEntry->Flink;
1537             continue;
1538         }
1539 
1540         /* Get the first section, the section count, and scan them all */
1541         Section = IMAGE_FIRST_SECTION(NtHeader);
1542         SectionCount = NtHeader->FileHeader.NumberOfSections;
1543         InitStart = 0;
1544         while (SectionCount > 0)
1545         {
1546             /* Assume failure */
1547             InitFound = FALSE;
1548 
1549             /* Is this the INIT section or a discardable section? */
1550             if ((strncmp((PCCH)Section->Name, "INIT", 5) == 0) ||
1551                 ((Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)))
1552             {
1553                 /* Remember this */
1554                 InitFound = TRUE;
1555                 InitSection = Section;
1556             }
1557 
1558             if (InitFound)
1559             {
1560                 /* Pick the biggest size -- either raw or virtual */
1561                 Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
1562 
1563                 /* Read the section alignment */
1564                 Alignment = NtHeader->OptionalHeader.SectionAlignment;
1565 
1566                 /* Get the start and end addresses */
1567                 InitStart = DllBase + Section->VirtualAddress;
1568                 InitEnd = ALIGN_UP_BY(InitStart + Size, Alignment);
1569 
1570                 /* Align the addresses to PAGE_SIZE */
1571                 InitStart = ALIGN_UP_BY(InitStart, PAGE_SIZE);
1572                 InitEnd = ALIGN_DOWN_BY(InitEnd, PAGE_SIZE);
1573 
1574                 /* Have we reached the last section? */
1575                 if (SectionCount == 1)
1576                 {
1577                     /* Remember this */
1578                     LastSection = Section;
1579                 }
1580                 else
1581                 {
1582                     /* We have not, loop all the sections */
1583                     LastSection = NULL;
1584                     do
1585                     {
1586                         /* Keep going until we find a non-discardable section range */
1587                         SectionCount--;
1588                         Section++;
1589                         if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
1590                         {
1591                             /* Discardable, so record it, then keep going */
1592                             LastSection = Section;
1593                         }
1594                         else
1595                         {
1596                             /* Non-contigous discard flag, or no flag, break out */
1597                             break;
1598                         }
1599                     }
1600                     while (SectionCount > 1);
1601                 }
1602 
1603                 /* Have we found a discardable or init section? */
1604                 if (LastSection)
1605                 {
1606                     /* Pick the biggest size -- either raw or virtual */
1607                     Size = max(LastSection->SizeOfRawData, LastSection->Misc.VirtualSize);
1608 
1609                     /* Use this as the end of the section address */
1610                     InitEnd = DllBase + LastSection->VirtualAddress + Size;
1611 
1612                     /* Have we reached the last section yet? */
1613                     if (SectionCount != 1)
1614                     {
1615                         /* Then align this accross the session boundary */
1616                         InitEnd = ALIGN_UP_BY(InitEnd, Alignment);
1617                         InitEnd = ALIGN_DOWN_BY(InitEnd, PAGE_SIZE);
1618                     }
1619                 }
1620 
1621                 /* Make sure we don't let the init section go past the image */
1622                 ImageEnd = DllBase + LdrEntry->SizeOfImage;
1623                 if (InitEnd > ImageEnd) InitEnd = ALIGN_UP_BY(ImageEnd, PAGE_SIZE);
1624 
1625                 /* Make sure we have a valid, non-zero init section */
1626                 if (InitStart < InitEnd)
1627                 {
1628                     /* Make sure we are not within this code itself */
1629                     if ((InitCode >= InitStart) && (InitCode < InitEnd))
1630                     {
1631                         /* Return it, we can't free ourselves now */
1632                         ASSERT(*StartVa == 0);
1633                         *StartVa = (PVOID)InitStart;
1634                         *EndVa = (PVOID)InitEnd;
1635                     }
1636                     else
1637                     {
1638                         /* This isn't us -- go ahead and free it */
1639                         ASSERT(MI_IS_PHYSICAL_ADDRESS((PVOID)InitStart) == FALSE);
1640                         DPRINT("Freeing init code: %p-%p ('%wZ' @%p : '%s')\n",
1641                                (PVOID)InitStart,
1642                                (PVOID)InitEnd,
1643                                &LdrEntry->BaseDllName,
1644                                LdrEntry->DllBase,
1645                                InitSection->Name);
1646                         MiFreeInitializationCode((PVOID)InitStart, (PVOID)InitEnd);
1647                     }
1648                 }
1649             }
1650 
1651             /* Move to the next section */
1652             SectionCount--;
1653             Section++;
1654         }
1655 
1656         /* Move to the next module */
1657         NextEntry = NextEntry->Flink;
1658     }
1659 
1660     /* Release the locks and return */
1661     ExReleaseResourceLite(&PsLoadedModuleResource);
1662     KeReleaseMutant(&MmSystemLoadLock, MUTANT_INCREMENT, FALSE, FALSE);
1663     KeLeaveCriticalRegion();
1664 }
1665 
1666 /*
1667  * Note: This function assumes that all discardable sections are at the end of
1668  * the PE file. It searches backwards until it finds the non-discardable section
1669  */
1670 VOID
1671 NTAPI
1672 MmFreeDriverInitialization(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
1673 {
1674     PMMPTE StartPte, EndPte;
1675     PFN_NUMBER PageCount;
1676     PVOID DllBase;
1677     ULONG i;
1678     PIMAGE_NT_HEADERS NtHeader;
1679     PIMAGE_SECTION_HEADER Section, DiscardSection;
1680 
1681     /* Get the base address and the page count */
1682     DllBase = LdrEntry->DllBase;
1683     PageCount = LdrEntry->SizeOfImage >> PAGE_SHIFT;
1684 
1685     /* Get the last PTE in this image */
1686     EndPte = MiAddressToPte(DllBase) + PageCount;
1687 
1688     /* Get the NT header */
1689     NtHeader = RtlImageNtHeader(DllBase);
1690     if (!NtHeader) return;
1691 
1692     /* Get the last section and loop each section backwards */
1693     Section = IMAGE_FIRST_SECTION(NtHeader) + NtHeader->FileHeader.NumberOfSections;
1694     DiscardSection = NULL;
1695     for (i = 0; i < NtHeader->FileHeader.NumberOfSections; i++)
1696     {
1697         /* Go back a section and check if it's discardable */
1698         Section--;
1699         if (Section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
1700         {
1701             /* It is, select it for freeing */
1702             DiscardSection = Section;
1703         }
1704         else
1705         {
1706             /* No more discardable sections exist, bail out */
1707             break;
1708         }
1709     }
1710 
1711     /* Bail out if there's nothing to free */
1712     if (!DiscardSection) return;
1713 
1714     /* Push the DLL base to the first disacrable section, and get its PTE */
1715     DllBase = (PVOID)ROUND_TO_PAGES((ULONG_PTR)DllBase + DiscardSection->VirtualAddress);
1716     ASSERT(MI_IS_PHYSICAL_ADDRESS(DllBase) == FALSE);
1717     StartPte = MiAddressToPte(DllBase);
1718 
1719     /* Check how many pages to free total */
1720     PageCount = (PFN_NUMBER)(EndPte - StartPte);
1721     if (!PageCount) return;
1722 
1723     /* Delete this many PTEs */
1724     MiDeleteSystemPageableVm(StartPte, PageCount, 0, NULL);
1725 }
1726 
1727 CODE_SEG("INIT")
1728 VOID
1729 NTAPI
1730 MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1731 {
1732     PLIST_ENTRY NextEntry;
1733     ULONG i = 0;
1734     PIMAGE_NT_HEADERS NtHeader;
1735     PLDR_DATA_TABLE_ENTRY LdrEntry;
1736     PIMAGE_FILE_HEADER FileHeader;
1737     BOOLEAN ValidRelocs;
1738     PIMAGE_DATA_DIRECTORY DataDirectory;
1739     PVOID DllBase, NewImageAddress;
1740     NTSTATUS Status;
1741     PMMPTE PointerPte, StartPte, LastPte;
1742     PFN_COUNT PteCount;
1743     PMMPFN Pfn1;
1744     MMPTE TempPte, OldPte;
1745 
1746     /* Loop driver list */
1747     for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
1748          NextEntry != &LoaderBlock->LoadOrderListHead;
1749          NextEntry = NextEntry->Flink)
1750     {
1751         /* Get the loader entry and NT header */
1752         LdrEntry = CONTAINING_RECORD(NextEntry,
1753                                      LDR_DATA_TABLE_ENTRY,
1754                                      InLoadOrderLinks);
1755         NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
1756 
1757         /* Debug info */
1758         DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
1759                 LdrEntry->DllBase,
1760                 (ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage,
1761                 &LdrEntry->FullDllName);
1762 
1763         /* Get the first PTE and the number of PTEs we'll need */
1764         PointerPte = StartPte = MiAddressToPte(LdrEntry->DllBase);
1765         PteCount = ROUND_TO_PAGES(LdrEntry->SizeOfImage) >> PAGE_SHIFT;
1766         LastPte = StartPte + PteCount;
1767 
1768 #if MI_TRACE_PFNS
1769         /* Loop the PTEs */
1770         while (PointerPte < LastPte)
1771         {
1772             ULONG len;
1773             ASSERT(PointerPte->u.Hard.Valid == 1);
1774             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
1775             len = wcslen(LdrEntry->BaseDllName.Buffer) * sizeof(WCHAR);
1776             snprintf(Pfn1->ProcessName, min(16, len), "%S", LdrEntry->BaseDllName.Buffer);
1777             PointerPte++;
1778         }
1779 #endif
1780         /* Skip kernel and HAL */
1781         /* ROS HACK: Skip BOOTVID/KDCOM too */
1782         i++;
1783         if (i <= 4) continue;
1784 
1785         /* Skip non-drivers */
1786         if (!NtHeader) continue;
1787 
1788         /* Get the file header and make sure we can relocate */
1789         FileHeader = &NtHeader->FileHeader;
1790         if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) continue;
1791         if (NtHeader->OptionalHeader.NumberOfRvaAndSizes <
1792             IMAGE_DIRECTORY_ENTRY_BASERELOC) continue;
1793 
1794         /* Everything made sense until now, check the relocation section too */
1795         DataDirectory = &NtHeader->OptionalHeader.
1796                         DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
1797         if (!DataDirectory->VirtualAddress)
1798         {
1799             /* We don't really have relocations */
1800             ValidRelocs = FALSE;
1801         }
1802         else
1803         {
1804             /* Make sure the size is valid */
1805             if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
1806                 LdrEntry->SizeOfImage)
1807             {
1808                 /* They're not, skip */
1809                 continue;
1810             }
1811 
1812             /* We have relocations */
1813             ValidRelocs = TRUE;
1814         }
1815 
1816         /* Remember the original address */
1817         DllBase = LdrEntry->DllBase;
1818 
1819         /* Loop the PTEs */
1820         PointerPte = StartPte;
1821         while (PointerPte < LastPte)
1822         {
1823             /* Mark the page modified in the PFN database */
1824             ASSERT(PointerPte->u.Hard.Valid == 1);
1825             Pfn1 = MiGetPfnEntry(PFN_FROM_PTE(PointerPte));
1826             ASSERT(Pfn1->u3.e1.Rom == 0);
1827             Pfn1->u3.e1.Modified = TRUE;
1828 
1829             /* Next */
1830             PointerPte++;
1831         }
1832 
1833         /* Now reserve system PTEs for the image */
1834         PointerPte = MiReserveSystemPtes(PteCount, SystemPteSpace);
1835         if (!PointerPte)
1836         {
1837             /* Shouldn't happen */
1838             ERROR_FATAL("[Mm0]: Couldn't allocate driver section!\n");
1839             return;
1840         }
1841 
1842         /* This is the new virtual address for the module */
1843         LastPte = PointerPte + PteCount;
1844         NewImageAddress = MiPteToAddress(PointerPte);
1845 
1846         /* Sanity check */
1847         DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
1848         ASSERT(ExpInitializationPhase == 0);
1849 
1850         /* Loop the new driver PTEs */
1851         TempPte = ValidKernelPte;
1852         while (PointerPte < LastPte)
1853         {
1854             /* Copy the old data */
1855             OldPte = *StartPte;
1856             ASSERT(OldPte.u.Hard.Valid == 1);
1857 
1858             /* Set page number from the loader's memory */
1859             TempPte.u.Hard.PageFrameNumber = OldPte.u.Hard.PageFrameNumber;
1860 
1861             /* Write it */
1862             MI_WRITE_VALID_PTE(PointerPte, TempPte);
1863 
1864             /* Move on */
1865             PointerPte++;
1866             StartPte++;
1867         }
1868 
1869         /* Update position */
1870         PointerPte -= PteCount;
1871 
1872         /* Sanity check */
1873         ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
1874 
1875         /* Set the image base to the address where the loader put it */
1876         NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
1877 
1878         /* Check if we had relocations */
1879         if (ValidRelocs)
1880         {
1881             /* Relocate the image */
1882             Status = LdrRelocateImageWithBias(NewImageAddress,
1883                                               0,
1884                                               "SYSLDR",
1885                                               STATUS_SUCCESS,
1886                                               STATUS_CONFLICTING_ADDRESSES,
1887                                               STATUS_INVALID_IMAGE_FORMAT);
1888             if (!NT_SUCCESS(Status))
1889             {
1890                 /* This shouldn't happen */
1891                 ERROR_FATAL("Relocations failed!\n");
1892                 return;
1893             }
1894         }
1895 
1896         /* Update the loader entry */
1897         LdrEntry->DllBase = NewImageAddress;
1898 
1899         /* Update the thunks */
1900         DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
1901         MiUpdateThunks(LoaderBlock,
1902                        DllBase,
1903                        NewImageAddress,
1904                        LdrEntry->SizeOfImage);
1905 
1906         /* Update the loader entry */
1907         LdrEntry->Flags |= LDRP_SYSTEM_MAPPED;
1908         LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
1909                                 NtHeader->OptionalHeader.AddressOfEntryPoint);
1910         LdrEntry->SizeOfImage = PteCount << PAGE_SHIFT;
1911 
1912         /* FIXME: We'll need to fixup the PFN linkage when switching to ARM3 */
1913     }
1914 }
1915 
1916 CODE_SEG("INIT")
1917 NTSTATUS
1918 NTAPI
1919 MiBuildImportsForBootDrivers(VOID)
1920 {
1921     PLIST_ENTRY NextEntry, NextEntry2;
1922     PLDR_DATA_TABLE_ENTRY LdrEntry, KernelEntry, HalEntry, LdrEntry2, LastEntry;
1923     PLDR_DATA_TABLE_ENTRY* EntryArray;
1924     UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
1925     UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
1926     PLOAD_IMPORTS LoadedImports;
1927     ULONG LoadedImportsSize, ImportSize;
1928     PULONG_PTR ImageThunk;
1929     ULONG_PTR DllBase, DllEnd;
1930     ULONG Modules = 0, i, j = 0;
1931     PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
1932 
1933     /* Initialize variables */
1934     KernelEntry = HalEntry = LastEntry = NULL;
1935 
1936     /* Loop the loaded module list... we are early enough that no lock is needed */
1937     NextEntry = PsLoadedModuleList.Flink;
1938     while (NextEntry != &PsLoadedModuleList)
1939     {
1940         /* Get the entry */
1941         LdrEntry = CONTAINING_RECORD(NextEntry,
1942                                      LDR_DATA_TABLE_ENTRY,
1943                                      InLoadOrderLinks);
1944 
1945         /* Check if it's the kernel or HAL */
1946         if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
1947         {
1948             /* Found it */
1949             KernelEntry = LdrEntry;
1950         }
1951         else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
1952         {
1953             /* Found it */
1954             HalEntry = LdrEntry;
1955         }
1956 
1957         /* Check if this is a driver DLL */
1958         if (LdrEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL)
1959         {
1960             /* Check if this is the HAL or kernel */
1961             if ((LdrEntry == HalEntry) || (LdrEntry == KernelEntry))
1962             {
1963                 /* Add a reference */
1964                 LdrEntry->LoadCount = 1;
1965             }
1966             else
1967             {
1968                 /* No referencing needed */
1969                 LdrEntry->LoadCount = 0;
1970             }
1971         }
1972         else
1973         {
1974             /* Add a reference for all other modules as well */
1975             LdrEntry->LoadCount = 1;
1976         }
1977 
1978         /* Remember this came from the loader */
1979         LdrEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
1980 
1981         /* Keep looping */
1982         NextEntry = NextEntry->Flink;
1983         Modules++;
1984     }
1985 
1986     /* We must have at least found the kernel and HAL */
1987     if (!(HalEntry) || (!KernelEntry)) return STATUS_NOT_FOUND;
1988 
1989     /* Allocate the list */
1990     EntryArray = ExAllocatePoolWithTag(PagedPool, Modules * sizeof(PVOID), TAG_LDR_IMPORTS);
1991     if (!EntryArray) return STATUS_INSUFFICIENT_RESOURCES;
1992 
1993     /* Loop the loaded module list again */
1994     NextEntry = PsLoadedModuleList.Flink;
1995     while (NextEntry != &PsLoadedModuleList)
1996     {
1997         /* Get the entry */
1998         LdrEntry = CONTAINING_RECORD(NextEntry,
1999                                      LDR_DATA_TABLE_ENTRY,
2000                                      InLoadOrderLinks);
2001 #ifdef _WORKING_LOADER_
2002         /* Get its imports */
2003         ImageThunk = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
2004                                                   TRUE,
2005                                                   IMAGE_DIRECTORY_ENTRY_IAT,
2006                                                   &ImportSize);
2007         if (!ImageThunk)
2008 #else
2009         /* Get its imports */
2010         ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
2011                                                         TRUE,
2012                                                         IMAGE_DIRECTORY_ENTRY_IMPORT,
2013                                                         &ImportSize);
2014         if (!ImportDescriptor)
2015 #endif
2016         {
2017             /* None present */
2018             LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
2019             NextEntry = NextEntry->Flink;
2020             continue;
2021         }
2022 
2023         /* Clear the list and count the number of IAT thunks */
2024         RtlZeroMemory(EntryArray, Modules * sizeof(PVOID));
2025 #ifdef _WORKING_LOADER_
2026         ImportSize /= sizeof(ULONG_PTR);
2027 
2028         /* Scan the thunks */
2029         for (i = 0, DllBase = 0, DllEnd = 0; i < ImportSize; i++, ImageThunk++)
2030 #else
2031         DllBase = DllEnd = i = 0;
2032         while ((ImportDescriptor->Name) &&
2033                (ImportDescriptor->OriginalFirstThunk))
2034         {
2035             /* Get the image thunk */
2036             ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
2037                                  ImportDescriptor->FirstThunk);
2038             while (*ImageThunk)
2039 #endif
2040             {
2041             /* Do we already have an address? */
2042             if (DllBase)
2043             {
2044                 /* Is the thunk in the same address? */
2045                 if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
2046                 {
2047                     /* Skip it, we already have a reference for it */
2048                     ASSERT(EntryArray[j]);
2049                     ImageThunk++;
2050                     continue;
2051                 }
2052             }
2053 
2054             /* Loop the loaded module list to locate this address owner */
2055             j = 0;
2056             NextEntry2 = PsLoadedModuleList.Flink;
2057             while (NextEntry2 != &PsLoadedModuleList)
2058             {
2059                 /* Get the entry */
2060                 LdrEntry2 = CONTAINING_RECORD(NextEntry2,
2061                                               LDR_DATA_TABLE_ENTRY,
2062                                               InLoadOrderLinks);
2063 
2064                 /* Get the address range for this module */
2065                 DllBase = (ULONG_PTR)LdrEntry2->DllBase;
2066                 DllEnd = DllBase + LdrEntry2->SizeOfImage;
2067 
2068                 /* Check if this IAT entry matches it */
2069                 if ((*ImageThunk >= DllBase) && (*ImageThunk < DllEnd))
2070                 {
2071                     /* Save it */
2072                     //DPRINT1("Found imported dll: %wZ\n", &LdrEntry2->BaseDllName);
2073                     EntryArray[j] = LdrEntry2;
2074                     break;
2075                 }
2076 
2077                 /* Keep searching */
2078                 NextEntry2 = NextEntry2->Flink;
2079                 j++;
2080             }
2081 
2082             /* Do we have a thunk outside the range? */
2083             if ((*ImageThunk < DllBase) || (*ImageThunk >= DllEnd))
2084             {
2085                 /* Could be 0... */
2086                 if (*ImageThunk)
2087                 {
2088                     /* Should not be happening */
2089                     ERROR_FATAL("Broken IAT entry for %p at %p (%lx)\n",
2090                                 LdrEntry, ImageThunk, *ImageThunk);
2091                 }
2092 
2093                 /* Reset if we hit this */
2094                 DllBase = 0;
2095             }
2096 #ifndef _WORKING_LOADER_
2097             ImageThunk++;
2098             }
2099 
2100             i++;
2101             ImportDescriptor++;
2102 #endif
2103         }
2104 
2105         /* Now scan how many imports we really have */
2106         for (i = 0, ImportSize = 0; i < Modules; i++)
2107         {
2108             /* Skip HAL and kernel */
2109             if ((EntryArray[i]) &&
2110                 (EntryArray[i] != HalEntry) &&
2111                 (EntryArray[i] != KernelEntry))
2112             {
2113                 /* A valid reference */
2114                 LastEntry = EntryArray[i];
2115                 ImportSize++;
2116             }
2117         }
2118 
2119         /* Do we have any imports after all? */
2120         if (!ImportSize)
2121         {
2122             /* No */
2123             LdrEntry->LoadedImports = MM_SYSLDR_NO_IMPORTS;
2124         }
2125         else if (ImportSize == 1)
2126         {
2127             /* A single entry import */
2128             LdrEntry->LoadedImports = (PVOID)((ULONG_PTR)LastEntry | MM_SYSLDR_SINGLE_ENTRY);
2129             LastEntry->LoadCount++;
2130         }
2131         else
2132         {
2133             /* We need an import table */
2134             LoadedImportsSize = ImportSize * sizeof(PVOID) + sizeof(SIZE_T);
2135             LoadedImports = ExAllocatePoolWithTag(PagedPool,
2136                                                   LoadedImportsSize,
2137                                                   TAG_LDR_IMPORTS);
2138             ASSERT(LoadedImports);
2139 
2140             /* Save the count */
2141             LoadedImports->Count = ImportSize;
2142 
2143             /* Now copy all imports */
2144             for (i = 0, j = 0; i < Modules; i++)
2145             {
2146                 /* Skip HAL and kernel */
2147                 if ((EntryArray[i]) &&
2148                     (EntryArray[i] != HalEntry) &&
2149                     (EntryArray[i] != KernelEntry))
2150                 {
2151                     /* A valid reference */
2152                     //DPRINT1("Found valid entry: %p\n", EntryArray[i]);
2153                     LoadedImports->Entry[j] = EntryArray[i];
2154                     EntryArray[i]->LoadCount++;
2155                     j++;
2156                 }
2157             }
2158 
2159             /* Should had as many entries as we expected */
2160             ASSERT(j == ImportSize);
2161             LdrEntry->LoadedImports = LoadedImports;
2162         }
2163 
2164         /* Next */
2165         NextEntry = NextEntry->Flink;
2166     }
2167 
2168     /* Free the initial array */
2169     ExFreePoolWithTag(EntryArray, TAG_LDR_IMPORTS);
2170 
2171     /* FIXME: Might not need to keep the HAL/Kernel imports around */
2172 
2173     /* Kernel and HAL are loaded at boot */
2174     KernelEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
2175     HalEntry->LoadedImports = MM_SYSLDR_BOOT_LOADED;
2176 
2177     /* All worked well */
2178     return STATUS_SUCCESS;
2179 }
2180 
2181 CODE_SEG("INIT")
2182 VOID
2183 NTAPI
2184 MiLocateKernelSections(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
2185 {
2186     ULONG_PTR DllBase;
2187     PIMAGE_NT_HEADERS NtHeaders;
2188     PIMAGE_SECTION_HEADER SectionHeader;
2189     ULONG Sections, Size;
2190 
2191     /* Get the kernel section header */
2192     DllBase = (ULONG_PTR)LdrEntry->DllBase;
2193     NtHeaders = RtlImageNtHeader((PVOID)DllBase);
2194     SectionHeader = IMAGE_FIRST_SECTION(NtHeaders);
2195 
2196     /* Loop all the sections */
2197     for (Sections = NtHeaders->FileHeader.NumberOfSections;
2198          Sections > 0; --Sections, ++SectionHeader)
2199     {
2200         /* Grab the size of the section */
2201         Size = max(SectionHeader->SizeOfRawData, SectionHeader->Misc.VirtualSize);
2202 
2203         /* Check for .RSRC section */
2204         if (*(PULONG)SectionHeader->Name == 'rsr.')
2205         {
2206             /* Remember the PTEs so we can modify them later */
2207             MiKernelResourceStartPte = MiAddressToPte(DllBase +
2208                                                       SectionHeader->VirtualAddress);
2209             MiKernelResourceEndPte = MiAddressToPte(ROUND_TO_PAGES(DllBase +
2210                                                     SectionHeader->VirtualAddress + Size));
2211         }
2212         else if (*(PULONG)SectionHeader->Name == 'LOOP')
2213         {
2214             /* POOLCODE vs. POOLMI */
2215             if (*(PULONG)&SectionHeader->Name[4] == 'EDOC')
2216             {
2217                 /* Found Ex* Pool code */
2218                 ExPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
2219                 ExPoolCodeEnd = ExPoolCodeStart + Size;
2220             }
2221             else if (*(PUSHORT)&SectionHeader->Name[4] == 'MI')
2222             {
2223                 /* Found Mm* Pool code */
2224                 MmPoolCodeStart = DllBase + SectionHeader->VirtualAddress;
2225                 MmPoolCodeEnd = MmPoolCodeStart + Size;
2226             }
2227         }
2228         else if ((*(PULONG)SectionHeader->Name == 'YSIM') &&
2229                  (*(PULONG)&SectionHeader->Name[4] == 'ETPS'))
2230         {
2231             /* Found MISYSPTE (Mm System PTE code) */
2232             MmPteCodeStart = DllBase + SectionHeader->VirtualAddress;
2233             MmPteCodeEnd = MmPteCodeStart + Size;
2234         }
2235     }
2236 }
2237 
2238 CODE_SEG("INIT")
2239 BOOLEAN
2240 NTAPI
2241 MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
2242 {
2243     PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
2244     PLIST_ENTRY ListHead, NextEntry;
2245     ULONG EntrySize;
2246 
2247     /* Setup the loaded module list and locks */
2248     ExInitializeResourceLite(&PsLoadedModuleResource);
2249     KeInitializeSpinLock(&PsLoadedModuleSpinLock);
2250     InitializeListHead(&PsLoadedModuleList);
2251 
2252     /* Get loop variables and the kernel entry */
2253     ListHead = &LoaderBlock->LoadOrderListHead;
2254     NextEntry = ListHead->Flink;
2255     LdrEntry = CONTAINING_RECORD(NextEntry,
2256                                  LDR_DATA_TABLE_ENTRY,
2257                                  InLoadOrderLinks);
2258     PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;
2259 
2260     /* Locate resource section, pool code, and system pte code */
2261     MiLocateKernelSections(LdrEntry);
2262 
2263     /* Loop the loader block */
2264     while (NextEntry != ListHead)
2265     {
2266         /* Get the loader entry */
2267         LdrEntry = CONTAINING_RECORD(NextEntry,
2268                                      LDR_DATA_TABLE_ENTRY,
2269                                      InLoadOrderLinks);
2270 
2271         /* FIXME: ROS HACK. Make sure this is a driver */
2272         if (!RtlImageNtHeader(LdrEntry->DllBase))
2273         {
2274             /* Skip this entry */
2275             NextEntry = NextEntry->Flink;
2276             continue;
2277         }
2278 
2279         /* Calculate the size we'll need and allocate a copy */
2280         EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
2281                     LdrEntry->BaseDllName.MaximumLength +
2282                     sizeof(UNICODE_NULL);
2283         NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
2284         if (!NewEntry) return FALSE;
2285 
2286         /* Copy the entry over */
2287         *NewEntry = *LdrEntry;
2288 
2289         /* Allocate the name */
2290         NewEntry->FullDllName.Buffer =
2291             ExAllocatePoolWithTag(PagedPool,
2292                                   LdrEntry->FullDllName.MaximumLength +
2293                                       sizeof(UNICODE_NULL),
2294                                   TAG_LDR_WSTR);
2295         if (!NewEntry->FullDllName.Buffer)
2296         {
2297             ExFreePoolWithTag(NewEntry, TAG_MODULE_OBJECT);
2298             return FALSE;
2299         }
2300 
2301         /* Set the base name */
2302         NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
2303 
2304         /* Copy the full and base name */
2305         RtlCopyMemory(NewEntry->FullDllName.Buffer,
2306                       LdrEntry->FullDllName.Buffer,
2307                       LdrEntry->FullDllName.MaximumLength);
2308         RtlCopyMemory(NewEntry->BaseDllName.Buffer,
2309                       LdrEntry->BaseDllName.Buffer,
2310                       LdrEntry->BaseDllName.MaximumLength);
2311 
2312         /* Null-terminate the base name */
2313         NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
2314                                      sizeof(WCHAR)] = UNICODE_NULL;
2315 
2316         /* Insert the entry into the list */
2317         InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
2318         NextEntry = NextEntry->Flink;
2319     }
2320 
2321     /* Build the import lists for the boot drivers */
2322     MiBuildImportsForBootDrivers();
2323 
2324     /* We're done */
2325     return TRUE;
2326 }
2327 
2328 BOOLEAN
2329 NTAPI
2330 MmChangeKernelResourceSectionProtection(IN ULONG_PTR ProtectionMask)
2331 {
2332     PMMPTE PointerPte;
2333     MMPTE TempPte;
2334 
2335     /* Don't do anything if the resource section is already writable */
2336     if (MiKernelResourceStartPte == NULL || MiKernelResourceEndPte == NULL)
2337         return FALSE;
2338 
2339     /* If the resource section is physical, we cannot change its protection */
2340     if (MI_IS_PHYSICAL_ADDRESS(MiPteToAddress(MiKernelResourceStartPte)))
2341         return FALSE;
2342 
2343     /* Loop the PTEs */
2344     for (PointerPte = MiKernelResourceStartPte; PointerPte < MiKernelResourceEndPte; ++PointerPte)
2345     {
2346         /* Read the PTE */
2347         TempPte = *PointerPte;
2348 
2349         /* Update the protection */
2350         MI_MAKE_HARDWARE_PTE_KERNEL(&TempPte, PointerPte, ProtectionMask, TempPte.u.Hard.PageFrameNumber);
2351         MI_UPDATE_VALID_PTE(PointerPte, TempPte);
2352     }
2353 
2354     /* Only flush the current processor's TLB */
2355     KeFlushCurrentTb();
2356     return TRUE;
2357 }
2358 
2359 VOID
2360 NTAPI
2361 MmMakeKernelResourceSectionWritable(VOID)
2362 {
2363     /* Don't do anything if the resource section is already writable */
2364     if (MiKernelResourceStartPte == NULL || MiKernelResourceEndPte == NULL)
2365         return;
2366 
2367     /* If the resource section is physical, we cannot change its protection */
2368     if (MI_IS_PHYSICAL_ADDRESS(MiPteToAddress(MiKernelResourceStartPte)))
2369         return;
2370 
2371     if (MmChangeKernelResourceSectionProtection(MM_READWRITE))
2372     {
2373         /*
2374          * Invalidate the cached resource section PTEs
2375          * so as to not change its protection again later.
2376          */
2377         MiKernelResourceStartPte = NULL;
2378         MiKernelResourceEndPte = NULL;
2379     }
2380 }
2381 
2382 LOGICAL
2383 NTAPI
2384 MiUseLargeDriverPage(IN ULONG NumberOfPtes,
2385                      IN OUT PVOID *ImageBaseAddress,
2386                      IN PUNICODE_STRING BaseImageName,
2387                      IN BOOLEAN BootDriver)
2388 {
2389     PLIST_ENTRY NextEntry;
2390     BOOLEAN DriverFound = FALSE;
2391     PMI_LARGE_PAGE_DRIVER_ENTRY LargePageDriverEntry;
2392     ASSERT(KeGetCurrentIrql () <= APC_LEVEL);
2393     ASSERT(*ImageBaseAddress >= MmSystemRangeStart);
2394 
2395 #ifdef _X86_
2396     if (!(KeFeatureBits & KF_LARGE_PAGE)) return FALSE;
2397     if (!(__readcr4() & CR4_PSE)) return FALSE;
2398 #endif
2399 
2400     /* Make sure there's enough system PTEs for a large page driver */
2401     if (MmTotalFreeSystemPtes[SystemPteSpace] < (16 * (PDE_MAPPED_VA >> PAGE_SHIFT)))
2402     {
2403         return FALSE;
2404     }
2405 
2406     /* This happens if the registry key had a "*" (wildcard) in it */
2407     if (MiLargePageAllDrivers == 0)
2408     {
2409         /* It didn't, so scan the list */
2410         NextEntry = MiLargePageDriverList.Flink;
2411         while (NextEntry != &MiLargePageDriverList)
2412         {
2413             /* Check if the driver name matches */
2414             LargePageDriverEntry = CONTAINING_RECORD(NextEntry,
2415                                                      MI_LARGE_PAGE_DRIVER_ENTRY,
2416                                                      Links);
2417             if (RtlEqualUnicodeString(BaseImageName,
2418                                       &LargePageDriverEntry->BaseName,
2419                                       TRUE))
2420             {
2421                 /* Enable large pages for this driver */
2422                 DriverFound = TRUE;
2423                 break;
2424             }
2425 
2426             /* Keep trying */
2427             NextEntry = NextEntry->Flink;
2428         }
2429 
2430         /* If we didn't find the driver, it doesn't need large pages */
2431         if (DriverFound == FALSE) return FALSE;
2432     }
2433 
2434     /* Nothing to do yet */
2435     DPRINT1("Large pages not supported!\n");
2436     return FALSE;
2437 }
2438 
2439 VOID
2440 NTAPI
2441 MiSetSystemCodeProtection(
2442     _In_ PMMPTE FirstPte,
2443     _In_ PMMPTE LastPte,
2444     _In_ ULONG Protection)
2445 {
2446     PMMPTE PointerPte;
2447     MMPTE TempPte;
2448 
2449     /* Loop the PTEs */
2450     for (PointerPte = FirstPte; PointerPte <= LastPte; PointerPte++)
2451     {
2452         /* Read the PTE */
2453         TempPte = *PointerPte;
2454 
2455         /* Make sure it's valid */
2456         if (TempPte.u.Hard.Valid != 1)
2457         {
2458             DPRINT1("CORE-16449: FirstPte=%p, LastPte=%p, Protection=%lx\n", FirstPte, LastPte, Protection);
2459             DPRINT1("CORE-16449: PointerPte=%p, TempPte=%lx\n", PointerPte, TempPte.u.Long);
2460             DPRINT1("CORE-16449: Please issue the 'mod' and 'bt' (KDBG) or 'lm' and 'kp' (WinDbg) commands. Then report this in Jira.\n");
2461             ASSERT(TempPte.u.Hard.Valid == 1);
2462             break;
2463         }
2464 
2465         /* Update the protection */
2466         TempPte.u.Hard.Write = BooleanFlagOn(Protection, IMAGE_SCN_MEM_WRITE);
2467 #if _MI_HAS_NO_EXECUTE
2468         TempPte.u.Hard.NoExecute = !BooleanFlagOn(Protection, IMAGE_SCN_MEM_EXECUTE);
2469 #endif
2470 
2471         MI_UPDATE_VALID_PTE(PointerPte, TempPte);
2472     }
2473 
2474     /* Flush it all */
2475     KeFlushEntireTb(TRUE, TRUE);
2476 }
2477 
2478 VOID
2479 NTAPI
2480 MiWriteProtectSystemImage(
2481     _In_ PVOID ImageBase)
2482 {
2483     PIMAGE_NT_HEADERS NtHeaders;
2484     PIMAGE_SECTION_HEADER SectionHeaders, Section;
2485     ULONG i;
2486     PVOID SectionBase, SectionEnd;
2487     ULONG SectionSize;
2488     ULONG Protection;
2489     PMMPTE FirstPte, LastPte;
2490 
2491     /* Check if the registry setting is on or not */
2492     if (MmEnforceWriteProtection == FALSE)
2493     {
2494         /* Ignore section protection */
2495         return;
2496     }
2497 
2498     /* Large page mapped images are not supported */
2499     NT_ASSERT(!MI_IS_PHYSICAL_ADDRESS(ImageBase));
2500 
2501     /* Session images are not yet supported */
2502     NT_ASSERT(!MI_IS_SESSION_ADDRESS(ImageBase));
2503 
2504     /* Get the NT headers */
2505     NtHeaders = RtlImageNtHeader(ImageBase);
2506     if (NtHeaders == NULL)
2507     {
2508         DPRINT1("Failed to get NT headers for image @ %p\n", ImageBase);
2509         return;
2510     }
2511 
2512     /* Don't touch NT4 drivers */
2513     if ((NtHeaders->OptionalHeader.MajorOperatingSystemVersion < 5) ||
2514         (NtHeaders->OptionalHeader.MajorSubsystemVersion < 5))
2515     {
2516         DPRINT1("Skipping NT 4 driver @ %p\n", ImageBase);
2517         return;
2518     }
2519 
2520     /* Get the section headers */
2521     SectionHeaders = IMAGE_FIRST_SECTION(NtHeaders);
2522 
2523     /* Get the base address of the first section */
2524     SectionBase = Add2Ptr(ImageBase, SectionHeaders[0].VirtualAddress);
2525 
2526     /* Start protecting the image header as R/W */
2527     FirstPte = MiAddressToPte(ImageBase);
2528     LastPte = MiAddressToPte(SectionBase) - 1;
2529     Protection = IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_WRITE;
2530     if (LastPte >= FirstPte)
2531     {
2532         MiSetSystemCodeProtection(FirstPte, LastPte, Protection);
2533     }
2534 
2535     /* Loop the sections */
2536     for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
2537     {
2538         /* Get the section base address and size */
2539         Section = &SectionHeaders[i];
2540         SectionBase = Add2Ptr(ImageBase, Section->VirtualAddress);
2541         SectionSize = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
2542 
2543         /* Get the first PTE of this section */
2544         FirstPte = MiAddressToPte(SectionBase);
2545 
2546         /* Check for overlap with the previous range */
2547         if (FirstPte == LastPte)
2548         {
2549             /* Combine the old and new protection by ORing them */
2550             Protection |= (Section->Characteristics & IMAGE_SCN_PROTECTION_MASK);
2551 
2552             /* Update the protection for this PTE */
2553             MiSetSystemCodeProtection(FirstPte, FirstPte, Protection);
2554 
2555             /* Skip this PTE */
2556             FirstPte++;
2557         }
2558 
2559         /* There can not be gaps! */
2560         NT_ASSERT(FirstPte == (LastPte + 1));
2561 
2562         /* Get the end of the section and the last PTE */
2563         SectionEnd = Add2Ptr(SectionBase, SectionSize - 1);
2564         NT_ASSERT(SectionEnd < Add2Ptr(ImageBase, NtHeaders->OptionalHeader.SizeOfImage));
2565         LastPte = MiAddressToPte(SectionEnd);
2566 
2567         /* If there are no more pages (after an overlap), skip this section */
2568         if (LastPte < FirstPte)
2569         {
2570             NT_ASSERT(FirstPte == (LastPte + 1));
2571             continue;
2572         }
2573 
2574         /* Get the section protection */
2575         Protection = (Section->Characteristics & IMAGE_SCN_PROTECTION_MASK);
2576 
2577         /* Update the protection for this section */
2578         MiSetSystemCodeProtection(FirstPte, LastPte, Protection);
2579     }
2580 
2581     /* Image should end with the last section */
2582     if (ALIGN_UP_POINTER_BY(SectionEnd, PAGE_SIZE) !=
2583         Add2Ptr(ImageBase, NtHeaders->OptionalHeader.SizeOfImage))
2584     {
2585         DPRINT1("ImageBase 0x%p ImageSize 0x%lx Section %u VA 0x%lx Raw 0x%lx virt 0x%lx\n",
2586             ImageBase,
2587             NtHeaders->OptionalHeader.SizeOfImage,
2588             i,
2589             Section->VirtualAddress,
2590             Section->SizeOfRawData,
2591             Section->Misc.VirtualSize);
2592     }
2593 }
2594 
2595 VOID
2596 NTAPI
2597 MiSetPagingOfDriver(IN PMMPTE PointerPte,
2598                     IN PMMPTE LastPte)
2599 {
2600 #ifdef ENABLE_MISETPAGINGOFDRIVER
2601     PVOID ImageBase;
2602     PETHREAD CurrentThread = PsGetCurrentThread();
2603     PFN_COUNT PageCount = 0;
2604     PFN_NUMBER PageFrameIndex;
2605     PMMPFN Pfn1;
2606 #endif // ENABLE_MISETPAGINGOFDRIVER
2607 
2608     PAGED_CODE();
2609 
2610 #ifndef ENABLE_MISETPAGINGOFDRIVER
2611     /* The page fault handler is broken and doesn't page back in! */
2612     DPRINT1("WARNING: MiSetPagingOfDriver() called, but paging is broken! ignoring!\n");
2613 #else  // ENABLE_MISETPAGINGOFDRIVER
2614     /* Get the driver's base address */
2615     ImageBase = MiPteToAddress(PointerPte);
2616     ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(ImageBase) == FALSE);
2617 
2618     /* If this is a large page, it's stuck in physical memory */
2619     if (MI_IS_PHYSICAL_ADDRESS(ImageBase)) return;
2620 
2621     /* Lock the working set */
2622     MiLockWorkingSet(CurrentThread, &MmSystemCacheWs);
2623 
2624     /* Loop the PTEs */
2625     while (PointerPte <= LastPte)
2626     {
2627         /* Check for valid PTE */
2628         if (PointerPte->u.Hard.Valid == 1)
2629         {
2630             PageFrameIndex = PFN_FROM_PTE(PointerPte);
2631             Pfn1 = MiGetPfnEntry(PageFrameIndex);
2632             ASSERT(Pfn1->u2.ShareCount == 1);
2633 
2634             /* No working sets in ReactOS yet */
2635             PageCount++;
2636         }
2637 
2638         ImageBase = (PVOID)((ULONG_PTR)ImageBase + PAGE_SIZE);
2639         PointerPte++;
2640     }
2641 
2642     /* Release the working set */
2643     MiUnlockWorkingSet(CurrentThread, &MmSystemCacheWs);
2644 
2645     /* Do we have any driver pages? */
2646     if (PageCount)
2647     {
2648         /* Update counters */
2649         InterlockedExchangeAdd((PLONG)&MmTotalSystemDriverPages, PageCount);
2650     }
2651 #endif // ENABLE_MISETPAGINGOFDRIVER
2652 }
2653 
2654 VOID
2655 NTAPI
2656 MiEnablePagingOfDriver(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
2657 {
2658     ULONG_PTR ImageBase;
2659     PIMAGE_NT_HEADERS NtHeaders;
2660     ULONG Sections, Alignment, Size;
2661     PIMAGE_SECTION_HEADER Section;
2662     PMMPTE PointerPte = NULL, LastPte = NULL;
2663     if (MmDisablePagingExecutive) return;
2664 
2665     /* Get the driver base address and its NT header */
2666     ImageBase = (ULONG_PTR)LdrEntry->DllBase;
2667     NtHeaders = RtlImageNtHeader((PVOID)ImageBase);
2668     if (!NtHeaders) return;
2669 
2670     /* Get the sections and their alignment */
2671     Sections = NtHeaders->FileHeader.NumberOfSections;
2672     Alignment = NtHeaders->OptionalHeader.SectionAlignment - 1;
2673 
2674     /* Loop each section */
2675     Section = IMAGE_FIRST_SECTION(NtHeaders);
2676     while (Sections)
2677     {
2678         /* Find PAGE or .edata */
2679         if ((*(PULONG)Section->Name == 'EGAP') ||
2680             (*(PULONG)Section->Name == 'ade.'))
2681         {
2682             /* Had we already done some work? */
2683             if (!PointerPte)
2684             {
2685                 /* Nope, setup the first PTE address */
2686                 PointerPte = MiAddressToPte(ROUND_TO_PAGES(ImageBase +
2687                                                            Section->VirtualAddress));
2688             }
2689 
2690             /* Compute the size */
2691             Size = max(Section->SizeOfRawData, Section->Misc.VirtualSize);
2692 
2693             /* Find the last PTE that maps this section */
2694             LastPte = MiAddressToPte(ImageBase +
2695                                      Section->VirtualAddress +
2696                                      Alignment + Size - PAGE_SIZE);
2697         }
2698         else
2699         {
2700             /* Had we found a section before? */
2701             if (PointerPte)
2702             {
2703                 /* Mark it as pageable */
2704                 MiSetPagingOfDriver(PointerPte, LastPte);
2705                 PointerPte = NULL;
2706             }
2707         }
2708 
2709         /* Keep searching */
2710         Sections--;
2711         Section++;
2712     }
2713 
2714     /* Handle the straggler */
2715     if (PointerPte) MiSetPagingOfDriver(PointerPte, LastPte);
2716 }
2717 
2718 BOOLEAN
2719 NTAPI
2720 MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
2721 {
2722     PIMAGE_NT_HEADERS NtHeader;
2723     PAGED_CODE();
2724 
2725     /* Get NT Headers */
2726     NtHeader = RtlImageNtHeader(BaseAddress);
2727     if (NtHeader)
2728     {
2729         /* Check if this image is only safe for UP while we have 2+ CPUs */
2730         if ((KeNumberProcessors > 1) &&
2731             (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
2732         {
2733             /* Fail */
2734             return FALSE;
2735         }
2736     }
2737 
2738     /* Otherwise, it's safe */
2739     return TRUE;
2740 }
2741 
2742 NTSTATUS
2743 NTAPI
2744 MmCheckSystemImage(IN HANDLE ImageHandle,
2745                    IN BOOLEAN PurgeSection)
2746 {
2747     NTSTATUS Status;
2748     HANDLE SectionHandle;
2749     PVOID ViewBase = NULL;
2750     SIZE_T ViewSize = 0;
2751     IO_STATUS_BLOCK IoStatusBlock;
2752     FILE_STANDARD_INFORMATION FileStandardInfo;
2753     KAPC_STATE ApcState;
2754     PIMAGE_NT_HEADERS NtHeaders;
2755     OBJECT_ATTRIBUTES ObjectAttributes;
2756     PAGED_CODE();
2757 
2758     /* Setup the object attributes */
2759     InitializeObjectAttributes(&ObjectAttributes,
2760                                NULL,
2761                                OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
2762                                NULL,
2763                                NULL);
2764 
2765     /* Create a section for the DLL */
2766     Status = ZwCreateSection(&SectionHandle,
2767                              SECTION_MAP_EXECUTE,
2768                              &ObjectAttributes,
2769                              NULL,
2770                              PAGE_EXECUTE,
2771                              SEC_IMAGE,
2772                              ImageHandle);
2773     if (!NT_SUCCESS(Status))
2774     {
2775         DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
2776         return Status;
2777     }
2778 
2779     /* Make sure we're in the system process */
2780     KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
2781 
2782     /* Map it */
2783     Status = ZwMapViewOfSection(SectionHandle,
2784                                 NtCurrentProcess(),
2785                                 &ViewBase,
2786                                 0,
2787                                 0,
2788                                 NULL,
2789                                 &ViewSize,
2790                                 ViewShare,
2791                                 0,
2792                                 PAGE_EXECUTE);
2793     if (!NT_SUCCESS(Status))
2794     {
2795         /* We failed, close the handle and return */
2796         DPRINT1("ZwMapViewOfSection failed with status 0x%x\n", Status);
2797         KeUnstackDetachProcess(&ApcState);
2798         ZwClose(SectionHandle);
2799         return Status;
2800     }
2801 
2802     /* Now query image information */
2803     Status = ZwQueryInformationFile(ImageHandle,
2804                                     &IoStatusBlock,
2805                                     &FileStandardInfo,
2806                                     sizeof(FileStandardInfo),
2807                                     FileStandardInformation);
2808     if (NT_SUCCESS(Status))
2809     {
2810         /* First, verify the checksum */
2811         if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
2812                                                  ViewSize,
2813                                                  FileStandardInfo.
2814                                                  EndOfFile.LowPart))
2815         {
2816             /* Set checksum failure */
2817             Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
2818             goto Fail;
2819         }
2820 
2821         /* Make sure it's a real image */
2822         NtHeaders = RtlImageNtHeader(ViewBase);
2823         if (!NtHeaders)
2824         {
2825             /* Set checksum failure */
2826             Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
2827             goto Fail;
2828         }
2829 
2830         /* Make sure it's for the correct architecture */
2831         if ((NtHeaders->FileHeader.Machine != IMAGE_FILE_MACHINE_NATIVE) ||
2832             (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC))
2833         {
2834             /* Set protection failure */
2835             Status = STATUS_INVALID_IMAGE_PROTECT;
2836             goto Fail;
2837         }
2838 
2839         /* Check that it's a valid SMP image if we have more then one CPU */
2840         if (!MmVerifyImageIsOkForMpUse(ViewBase))
2841         {
2842             /* Otherwise it's not the right image */
2843             Status = STATUS_IMAGE_MP_UP_MISMATCH;
2844         }
2845     }
2846 
2847     /* Unmap the section, close the handle, and return status */
2848 Fail:
2849     ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
2850     KeUnstackDetachProcess(&ApcState);
2851     ZwClose(SectionHandle);
2852     return Status;
2853 }
2854 
2855 
2856 PVOID
2857 NTAPI
2858 LdrpFetchAddressOfSecurityCookie(PVOID BaseAddress, ULONG SizeOfImage)
2859 {
2860     PIMAGE_LOAD_CONFIG_DIRECTORY ConfigDir;
2861     ULONG DirSize;
2862     PVOID Cookie = NULL;
2863 
2864     /* Check NT header first */
2865     if (!RtlImageNtHeader(BaseAddress)) return NULL;
2866 
2867     /* Get the pointer to the config directory */
2868     ConfigDir = RtlImageDirectoryEntryToData(BaseAddress,
2869                                              TRUE,
2870                                              IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG,
2871                                              &DirSize);
2872 
2873     /* Check for sanity */
2874     if (!ConfigDir ||
2875         DirSize < FIELD_OFFSET(IMAGE_LOAD_CONFIG_DIRECTORY, SEHandlerTable) ||  /* SEHandlerTable is after SecurityCookie */
2876         (ConfigDir->Size != DirSize))
2877     {
2878         /* Invalid directory*/
2879         return NULL;
2880     }
2881 
2882     /* Now get the cookie */
2883     Cookie = (PVOID)ConfigDir->SecurityCookie;
2884 
2885     /* Check this cookie */
2886     if ((PCHAR)Cookie <= (PCHAR)BaseAddress ||
2887         (PCHAR)Cookie >= (PCHAR)BaseAddress + SizeOfImage)
2888     {
2889         Cookie = NULL;
2890     }
2891 
2892     /* Return validated security cookie */
2893     return Cookie;
2894 }
2895 
2896 PVOID
2897 NTAPI
2898 LdrpInitSecurityCookie(PLDR_DATA_TABLE_ENTRY LdrEntry)
2899 {
2900     PULONG_PTR Cookie;
2901     ULONG_PTR NewCookie;
2902 
2903     /* Fetch address of the cookie */
2904     Cookie = LdrpFetchAddressOfSecurityCookie(LdrEntry->DllBase, LdrEntry->SizeOfImage);
2905 
2906     if (Cookie)
2907     {
2908         /* Check if it's a default one */
2909         if ((*Cookie == DEFAULT_SECURITY_COOKIE) ||
2910             (*Cookie == 0))
2911         {
2912             LARGE_INTEGER Counter = KeQueryPerformanceCounter(NULL);
2913             /* The address should be unique */
2914             NewCookie = (ULONG_PTR)Cookie;
2915 
2916             /* We just need a simple tick, don't care about precision and whatnot */
2917             NewCookie ^= (ULONG_PTR)Counter.LowPart;
2918 
2919             /* If the result is 0 or the same as we got, just add one to the default value */
2920             if ((NewCookie == 0) || (NewCookie == *Cookie))
2921             {
2922                 NewCookie = DEFAULT_SECURITY_COOKIE + 1;
2923             }
2924 
2925             /* Set the new cookie value */
2926             *Cookie = NewCookie;
2927         }
2928     }
2929 
2930     return Cookie;
2931 }
2932 
2933 NTSTATUS
2934 NTAPI
2935 MmLoadSystemImage(IN PUNICODE_STRING FileName,
2936                   IN PUNICODE_STRING NamePrefix OPTIONAL,
2937                   IN PUNICODE_STRING LoadedName OPTIONAL,
2938                   IN ULONG Flags,
2939                   OUT PVOID *ModuleObject,
2940                   OUT PVOID *ImageBaseAddress)
2941 {
2942     PVOID ModuleLoadBase = NULL;
2943     NTSTATUS Status;
2944     HANDLE FileHandle = NULL;
2945     OBJECT_ATTRIBUTES ObjectAttributes;
2946     IO_STATUS_BLOCK IoStatusBlock;
2947     PIMAGE_NT_HEADERS NtHeader;
2948     UNICODE_STRING BaseName, BaseDirectory, PrefixName;
2949     PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
2950     ULONG EntrySize, DriverSize;
2951     PLOAD_IMPORTS LoadedImports = MM_SYSLDR_NO_IMPORTS;
2952     PCHAR MissingApiName, Buffer;
2953     PWCHAR MissingDriverName, PrefixedBuffer = NULL;
2954     HANDLE SectionHandle;
2955     ACCESS_MASK DesiredAccess;
2956     PSECTION Section = NULL;
2957     BOOLEAN LockOwned = FALSE;
2958     PLIST_ENTRY NextEntry;
2959     IMAGE_INFO ImageInfo;
2960 
2961     PAGED_CODE();
2962 
2963     /* Detect session-load */
2964     if (Flags)
2965     {
2966         /* Sanity checks */
2967         ASSERT(NamePrefix == NULL);
2968         ASSERT(LoadedName == NULL);
2969 
2970         /* Make sure the process is in session too */
2971         if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
2972     }
2973 
2974     /* Allocate a buffer we'll use for names */
2975     Buffer = ExAllocatePoolWithTag(NonPagedPool,
2976                                    MAXIMUM_FILENAME_LENGTH,
2977                                    TAG_LDR_WSTR);
2978     if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
2979 
2980     /* Check for a separator */
2981     if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
2982     {
2983         PWCHAR p;
2984         ULONG BaseLength;
2985 
2986         /* Loop the path until we get to the base name */
2987         p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
2988         while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
2989 
2990         /* Get the length */
2991         BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
2992         BaseLength *= sizeof(WCHAR);
2993 
2994         /* Setup the string */
2995         BaseName.Length = (USHORT)BaseLength;
2996         BaseName.Buffer = p;
2997     }
2998     else
2999     {
3000         /* Otherwise, we already have a base name */
3001         BaseName.Length = FileName->Length;
3002         BaseName.Buffer = FileName->Buffer;
3003     }
3004 
3005     /* Setup the maximum length */
3006     BaseName.MaximumLength = BaseName.Length;
3007 
3008     /* Now compute the base directory */
3009     BaseDirectory = *FileName;
3010     BaseDirectory.Length -= BaseName.Length;
3011     BaseDirectory.MaximumLength = BaseDirectory.Length;
3012 
3013     /* And the prefix, which for now is just the name itself */
3014     PrefixName = *FileName;
3015 
3016     /* Check if we have a prefix */
3017     if (NamePrefix)
3018     {
3019         /* Check if "directory + prefix" is too long for the string */
3020         Status = RtlUShortAdd(BaseDirectory.Length,
3021                               NamePrefix->Length,
3022                               &PrefixName.MaximumLength);
3023         if (!NT_SUCCESS(Status))
3024         {
3025             Status = STATUS_INVALID_PARAMETER;
3026             goto Quickie;
3027         }
3028 
3029         /* Check if "directory + prefix + basename" is too long for the string */
3030         Status = RtlUShortAdd(PrefixName.MaximumLength,
3031                               BaseName.Length,
3032                               &PrefixName.MaximumLength);
3033         if (!NT_SUCCESS(Status))
3034         {
3035             Status = STATUS_INVALID_PARAMETER;
3036             goto Quickie;
3037         }
3038 
3039         /* Allocate the buffer exclusively used for prefixed name */
3040         PrefixedBuffer = ExAllocatePoolWithTag(PagedPool,
3041                                                PrefixName.MaximumLength,
3042                                                TAG_LDR_WSTR);
3043         if (!PrefixedBuffer)
3044         {
3045             Status = STATUS_INSUFFICIENT_RESOURCES;
3046             goto Quickie;
3047         }
3048 
3049         /* Clear out the prefixed name string */
3050         PrefixName.Buffer = PrefixedBuffer;
3051         PrefixName.Length = 0;
3052 
3053         /* Concatenate the strings */
3054         RtlAppendUnicodeStringToString(&PrefixName, &BaseDirectory);
3055         RtlAppendUnicodeStringToString(&PrefixName, NamePrefix);
3056         RtlAppendUnicodeStringToString(&PrefixName, &BaseName);
3057 
3058         /* Now the base name of the image becomes the prefixed version */
3059         BaseName.Buffer = &(PrefixName.Buffer[BaseDirectory.Length / sizeof(WCHAR)]);
3060         BaseName.Length += NamePrefix->Length;
3061         BaseName.MaximumLength = (PrefixName.MaximumLength - BaseDirectory.Length);
3062     }
3063 
3064     /* Check if we already have a name, use it instead */
3065     if (LoadedName) BaseName = *LoadedName;
3066 
3067     /* Check for loader snap debugging */
3068     if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS)
3069     {
3070         /* Print out standard string */
3071         DPRINT1("MM:SYSLDR Loading %wZ (%wZ) %s\n",
3072                 &PrefixName, &BaseName, Flags ? "in session space" : "");
3073     }
3074 
3075     /* Acquire the load lock */
3076 LoaderScan:
3077     ASSERT(LockOwned == FALSE);
3078     LockOwned = TRUE;
3079     KeEnterCriticalRegion();
3080     KeWaitForSingleObject(&MmSystemLoadLock,
3081                           WrVirtualMemory,
3082                           KernelMode,
3083                           FALSE,
3084                           NULL);
3085 
3086     /* Scan the module list */
3087     NextEntry = PsLoadedModuleList.Flink;
3088     while (NextEntry != &PsLoadedModuleList)
3089     {
3090         /* Get the entry and compare the names */
3091         LdrEntry = CONTAINING_RECORD(NextEntry,
3092                                      LDR_DATA_TABLE_ENTRY,
3093                                      InLoadOrderLinks);
3094         if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
3095         {
3096             /* Found it, break out */
3097             break;
3098         }
3099 
3100         /* Keep scanning */
3101         NextEntry = NextEntry->Flink;
3102     }
3103 
3104     /* Check if we found the image */
3105     if (NextEntry != &PsLoadedModuleList)
3106     {
3107         /* Check if we had already mapped a section */
3108         if (Section)
3109         {
3110             /* Dereference and clear */
3111             ObDereferenceObject(Section);
3112             Section = NULL;
3113         }
3114 
3115         /* Check if this was supposed to be a session load */
3116         if (!Flags)
3117         {
3118             /* It wasn't, so just return the data */
3119             *ModuleObject = LdrEntry;
3120             *ImageBaseAddress = LdrEntry->DllBase;
3121             Status = STATUS_IMAGE_ALREADY_LOADED;
3122         }
3123         else
3124         {
3125             /* We don't support session loading yet */
3126             UNIMPLEMENTED_DBGBREAK("Unsupported Session-Load!\n");
3127             Status = STATUS_NOT_IMPLEMENTED;
3128         }
3129 
3130         /* Do cleanup */
3131         goto Quickie;
3132     }
3133     else if (!Section)
3134     {
3135         /* It wasn't loaded, and we didn't have a previous attempt */
3136         KeReleaseMutant(&MmSystemLoadLock, MUTANT_INCREMENT, FALSE, FALSE);
3137         KeLeaveCriticalRegion();
3138         LockOwned = FALSE;
3139 
3140         /* Check if KD is enabled */
3141         if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
3142         {
3143             /* FIXME: Attempt to get image from KD */
3144         }
3145 
3146         /* We don't have a valid entry */
3147         LdrEntry = NULL;
3148 
3149         /* Setup image attributes */
3150         InitializeObjectAttributes(&ObjectAttributes,
3151                                    FileName,
3152                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3153                                    NULL,
3154                                    NULL);
3155 
3156         /* Open the image */
3157         Status = ZwOpenFile(&FileHandle,
3158                             FILE_EXECUTE,
3159                             &ObjectAttributes,
3160                             &IoStatusBlock,
3161                             FILE_SHARE_READ | FILE_SHARE_DELETE,
3162                             0);
3163         if (!NT_SUCCESS(Status))
3164         {
3165             DPRINT1("ZwOpenFile failed for '%wZ' with status 0x%x\n",
3166                     FileName, Status);
3167             goto Quickie;
3168         }
3169 
3170         /* Validate it */
3171         Status = MmCheckSystemImage(FileHandle, FALSE);
3172         if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
3173             (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
3174             (Status == STATUS_INVALID_IMAGE_PROTECT))
3175         {
3176             /* Fail loading */
3177             goto Quickie;
3178         }
3179 
3180         /* Check if this is a session-load */
3181         if (Flags)
3182         {
3183             /* Then we only need read and execute */
3184             DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
3185         }
3186         else
3187         {
3188             /* Otherwise, we can allow write access */
3189             DesiredAccess = SECTION_ALL_ACCESS;
3190         }
3191 
3192         /* Initialize the attributes for the section */
3193         InitializeObjectAttributes(&ObjectAttributes,
3194                                    NULL,
3195                                    OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
3196                                    NULL,
3197                                    NULL);
3198 
3199         /* Create the section */
3200         Status = ZwCreateSection(&SectionHandle,
3201                                  DesiredAccess,
3202                                  &ObjectAttributes,
3203                                  NULL,
3204                                  PAGE_EXECUTE,
3205                                  SEC_IMAGE,
3206                                  FileHandle);
3207         if (!NT_SUCCESS(Status))
3208         {
3209             DPRINT1("ZwCreateSection failed with status 0x%x\n", Status);
3210             goto Quickie;
3211         }
3212 
3213         /* Now get the section pointer */
3214         Status = ObReferenceObjectByHandle(SectionHandle,
3215                                            SECTION_MAP_EXECUTE,
3216                                            MmSectionObjectType,
3217                                            KernelMode,
3218                                            (PVOID*)&Section,
3219                                            NULL);
3220         ZwClose(SectionHandle);
3221         if (!NT_SUCCESS(Status)) goto Quickie;
3222 
3223         /* Check if this was supposed to be a session-load */
3224         if (Flags)
3225         {
3226             /* We don't support session loading yet */
3227             UNIMPLEMENTED_DBGBREAK("Unsupported Session-Load!\n");
3228             goto Quickie;
3229         }
3230 
3231         /* Check the loader list again, we should end up in the path below */
3232         goto LoaderScan;
3233     }
3234     else
3235     {
3236         /* We don't have a valid entry */
3237         LdrEntry = NULL;
3238     }
3239 
3240     /* Load the image */
3241     Status = MiLoadImageSection(&Section,
3242                                 &ModuleLoadBase,
3243                                 FileName,
3244                                 FALSE,
3245                                 NULL);
3246     ASSERT(Status != STATUS_ALREADY_COMMITTED);
3247 
3248     /* Get the size of the driver */
3249     DriverSize = ((PMM_IMAGE_SECTION_OBJECT)Section->Segment)->ImageInformation.ImageFileSize;
3250 
3251     /* Make sure we're not being loaded into session space */
3252     if (!Flags)
3253     {
3254         /* Check for success */
3255         if (NT_SUCCESS(Status))
3256         {
3257             /* Support large pages for drivers */
3258             MiUseLargeDriverPage(DriverSize / PAGE_SIZE,
3259                                  &ModuleLoadBase,
3260                                  &BaseName,
3261                                  TRUE);
3262         }
3263 
3264         /* Dereference the section */
3265         ObDereferenceObject(Section);
3266         Section = NULL;
3267     }
3268 
3269     /* Check for failure of the load earlier */
3270     if (!NT_SUCCESS(Status))
3271     {
3272         DPRINT1("MiLoadImageSection failed with status 0x%x\n", Status);
3273         goto Quickie;
3274     }
3275 
3276     /* Relocate the driver */
3277     Status = LdrRelocateImageWithBias(ModuleLoadBase,
3278                                       0,
3279                                       "SYSLDR",
3280                                       STATUS_SUCCESS,
3281                                       STATUS_CONFLICTING_ADDRESSES,
3282                                       STATUS_INVALID_IMAGE_FORMAT);
3283     if (!NT_SUCCESS(Status))
3284     {
3285         DPRINT1("LdrRelocateImageWithBias failed with status 0x%x\n", Status);
3286         goto Quickie;
3287     }
3288 
3289     /* Get the NT Header */
3290     NtHeader = RtlImageNtHeader(ModuleLoadBase);
3291 
3292     /* Calculate the size we'll need for the entry and allocate it */
3293     EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
3294                 BaseName.Length +
3295                 sizeof(UNICODE_NULL);
3296 
3297     /* Allocate the entry */
3298     LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
3299     if (!LdrEntry)
3300     {
3301         /* Fail */
3302         Status = STATUS_INSUFFICIENT_RESOURCES;
3303         goto Quickie;
3304     }
3305 
3306     /* Setup the entry */
3307     LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
3308     LdrEntry->LoadCount = 1;
3309     LdrEntry->LoadedImports = LoadedImports;
3310     LdrEntry->PatchInformation = NULL;
3311 
3312     /* Check the version */
3313     if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
3314         (NtHeader->OptionalHeader.MajorImageVersion >= 5))
3315     {
3316         /* Mark this image as a native image */
3317         LdrEntry->Flags |= LDRP_ENTRY_NATIVE;
3318     }
3319 
3320     /* Setup the rest of the entry */
3321     LdrEntry->DllBase = ModuleLoadBase;
3322     LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
3323                                    NtHeader->OptionalHeader.AddressOfEntryPoint);
3324     LdrEntry->SizeOfImage = DriverSize;
3325     LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
3326     LdrEntry->SectionPointer = Section;
3327 
3328     /* Now write the DLL name */
3329     LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
3330     LdrEntry->BaseDllName.Length = BaseName.Length;
3331     LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
3332 
3333     /* Copy and null-terminate it */
3334     RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
3335                   BaseName.Buffer,
3336                   BaseName.Length);
3337     LdrEntry->BaseDllName.Buffer[BaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
3338 
3339     /* Now allocate the full name */
3340     LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
3341                                                          PrefixName.Length +
3342                                                          sizeof(UNICODE_NULL),
3343                                                          TAG_LDR_WSTR);
3344     if (!LdrEntry->FullDllName.Buffer)
3345     {
3346         /* Don't fail, just set it to zero */
3347         LdrEntry->FullDllName.Length = 0;
3348         LdrEntry->FullDllName.MaximumLength = 0;
3349     }
3350     else
3351     {
3352         /* Set it up */
3353         LdrEntry->FullDllName.Length = PrefixName.Length;
3354         LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
3355 
3356         /* Copy and null-terminate */
3357         RtlCopyMemory(LdrEntry->FullDllName.Buffer,
3358                       PrefixName.Buffer,
3359                       PrefixName.Length);
3360         LdrEntry->FullDllName.Buffer[PrefixName.Length / sizeof(WCHAR)] = UNICODE_NULL;
3361     }
3362 
3363     /* Add the entry */
3364     MiProcessLoaderEntry(LdrEntry, TRUE);
3365 
3366     /* Resolve imports */
3367     MissingApiName = Buffer;
3368     MissingDriverName = NULL;
3369     Status = MiResolveImageReferences(ModuleLoadBase,
3370                                       &BaseDirectory,
3371                                       NULL,
3372                                       &MissingApiName,
3373                                       &MissingDriverName,
3374                                       &LoadedImports);
3375     if (!NT_SUCCESS(Status))
3376     {
3377         BOOLEAN NeedToFreeString = FALSE;
3378 
3379         /* If the lowest bit is set to 1, this is a hint that we need to free */
3380         if (*(ULONG_PTR*)&MissingDriverName & 1)
3381         {
3382             NeedToFreeString = TRUE;
3383             *(ULONG_PTR*)&MissingDriverName &= ~1;
3384         }
3385 
3386         DPRINT1("MiResolveImageReferences failed with status 0x%x\n", Status);
3387         DPRINT1(" Missing driver '%ls', missing API '%s'\n",
3388                 MissingDriverName, MissingApiName);
3389 
3390         if (NeedToFreeString)
3391         {
3392             ExFreePoolWithTag(MissingDriverName, TAG_LDR_WSTR);
3393         }
3394 
3395         /* Fail */
3396         MiProcessLoaderEntry(LdrEntry, FALSE);
3397 
3398         /* Check if we need to free the name */
3399         if (LdrEntry->FullDllName.Buffer)
3400         {
3401             /* Free it */
3402             ExFreePoolWithTag(LdrEntry->FullDllName.Buffer, TAG_LDR_WSTR);
3403         }
3404 
3405         /* Free the entry itself */
3406         ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
3407         LdrEntry = NULL;
3408         goto Quickie;
3409     }
3410 
3411     /* Update the loader entry */
3412     LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
3413                         LDRP_ENTRY_PROCESSED |
3414                         LDRP_MM_LOADED);
3415     LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
3416     LdrEntry->LoadedImports = LoadedImports;
3417 
3418     /* FIXME: Call driver verifier's loader function */
3419 
3420     /* Write-protect the system image */
3421     MiWriteProtectSystemImage(LdrEntry->DllBase);
3422 
3423     /* Initialize the security cookie (Win7 is not doing this yet!) */
3424     LdrpInitSecurityCookie(LdrEntry);
3425 
3426     /* Check if notifications are enabled */
3427     if (PsImageNotifyEnabled)
3428     {
3429         /* Fill out the notification data */
3430         ImageInfo.Properties = 0;
3431         ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
3432         ImageInfo.SystemModeImage = TRUE;
3433         ImageInfo.ImageSize = LdrEntry->SizeOfImage;
3434         ImageInfo.ImageBase = LdrEntry->DllBase;
3435         ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;
3436 
3437         /* Send the notification */
3438         PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
3439     }
3440 
3441 #ifdef __ROS_ROSSYM__
3442     /* MiCacheImageSymbols doesn't detect rossym */
3443     if (TRUE)
3444 #else
3445     /* Check if there's symbols */
3446     if (MiCacheImageSymbols(LdrEntry->DllBase))
3447 #endif
3448     {
3449         UNICODE_STRING UnicodeTemp;
3450         STRING AnsiTemp;
3451 
3452         /* Check if the system root is present */
3453         if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
3454             !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
3455         {
3456             /* Add the system root */
3457             UnicodeTemp = PrefixName;
3458             UnicodeTemp.Buffer += 11;
3459             UnicodeTemp.Length -= (11 * sizeof(WCHAR));
3460             RtlStringCbPrintfA(Buffer,
3461                                MAXIMUM_FILENAME_LENGTH,
3462                                "%ws%wZ",
3463                                &SharedUserData->NtSystemRoot[2],
3464                                &UnicodeTemp);
3465         }
3466         else
3467         {
3468             /* Build the name */
3469             RtlStringCbPrintfA(Buffer, MAXIMUM_FILENAME_LENGTH,
3470                                "%wZ", &BaseName);
3471         }
3472 
3473         /* Setup the ANSI string */
3474         RtlInitString(&AnsiTemp, Buffer);
3475 
3476         /* Notify the debugger */
3477         DbgLoadImageSymbols(&AnsiTemp,
3478                             LdrEntry->DllBase,
3479                             (ULONG_PTR)PsGetCurrentProcessId());
3480         LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
3481     }
3482 
3483     /* Page the driver */
3484     ASSERT(Section == NULL);
3485     MiEnablePagingOfDriver(LdrEntry);
3486 
3487     /* Return pointers */
3488     *ModuleObject = LdrEntry;
3489     *ImageBaseAddress = LdrEntry->DllBase;
3490 
3491 Quickie:
3492     /* Check if we have the lock acquired */
3493     if (LockOwned)
3494     {
3495         /* Release the lock */
3496         KeReleaseMutant(&MmSystemLoadLock, MUTANT_INCREMENT, FALSE, FALSE);
3497         KeLeaveCriticalRegion();
3498         LockOwned = FALSE;
3499     }
3500 
3501     /* If we have a file handle, close it */
3502     if (FileHandle) ZwClose(FileHandle);
3503 
3504     /* If we have allocated a prefixed name buffer, free it */
3505     if (PrefixedBuffer) ExFreePoolWithTag(PrefixedBuffer, TAG_LDR_WSTR);
3506 
3507     /* Free the name buffer and return status */
3508     ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
3509     return Status;
3510 }
3511 
3512 PLDR_DATA_TABLE_ENTRY
3513 NTAPI
3514 MiLookupDataTableEntry(IN PVOID Address)
3515 {
3516     PLDR_DATA_TABLE_ENTRY LdrEntry, FoundEntry = NULL;
3517     PLIST_ENTRY NextEntry;
3518     PAGED_CODE();
3519 
3520     /* Loop entries */
3521     NextEntry = PsLoadedModuleList.Flink;
3522     do
3523     {
3524         /* Get the loader entry */
3525         LdrEntry =  CONTAINING_RECORD(NextEntry,
3526                                       LDR_DATA_TABLE_ENTRY,
3527                                       InLoadOrderLinks);
3528 
3529         /* Check if the address matches */
3530         if ((Address >= LdrEntry->DllBase) &&
3531             (Address < (PVOID)((ULONG_PTR)LdrEntry->DllBase +
3532                                LdrEntry->SizeOfImage)))
3533         {
3534             /* Found a match */
3535             FoundEntry = LdrEntry;
3536             break;
3537         }
3538 
3539         /* Move on */
3540         NextEntry = NextEntry->Flink;
3541     } while(NextEntry != &PsLoadedModuleList);
3542 
3543     /* Return the entry */
3544     return FoundEntry;
3545 }
3546 
3547 /* PUBLIC FUNCTIONS ***********************************************************/
3548 
3549 /*
3550  * @implemented
3551  */
3552 PVOID
3553 NTAPI
3554 MmPageEntireDriver(IN PVOID AddressWithinSection)
3555 {
3556     PMMPTE StartPte, EndPte;
3557     PLDR_DATA_TABLE_ENTRY LdrEntry;
3558     PAGED_CODE();
3559 
3560     /* Get the loader entry */
3561     LdrEntry = MiLookupDataTableEntry(AddressWithinSection);
3562     if (!LdrEntry) return NULL;
3563 
3564     /* Check if paging of kernel mode is disabled or if the driver is mapped as an image */
3565     if ((MmDisablePagingExecutive) || (LdrEntry->SectionPointer))
3566     {
3567         /* Don't do anything, just return the base address */
3568         return LdrEntry->DllBase;
3569     }
3570 
3571     /* Wait for active DPCs to finish before we page out the driver */
3572     KeFlushQueuedDpcs();
3573 
3574     /* Get the PTE range for the whole driver image */
3575     StartPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase);
3576     EndPte = MiAddressToPte((ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage);
3577 
3578     /* Enable paging for the PTE range */
3579     ASSERT(MI_IS_SESSION_IMAGE_ADDRESS(AddressWithinSection) == FALSE);
3580     MiSetPagingOfDriver(StartPte, EndPte);
3581 
3582     /* Return the base address */
3583     return LdrEntry->DllBase;
3584 }
3585 
3586 /*
3587  * @unimplemented
3588  */
3589 VOID
3590 NTAPI
3591 MmResetDriverPaging(IN PVOID AddressWithinSection)
3592 {
3593     UNIMPLEMENTED;
3594 }
3595 
3596 /*
3597  * @implemented
3598  */
3599 PVOID
3600 NTAPI
3601 MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
3602 {
3603     PVOID ProcAddress = NULL;
3604     ANSI_STRING AnsiRoutineName;
3605     NTSTATUS Status;
3606     PLIST_ENTRY NextEntry;
3607     PLDR_DATA_TABLE_ENTRY LdrEntry;
3608     BOOLEAN Found = FALSE;
3609     UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
3610     UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
3611     ULONG Modules = 0;
3612 
3613     /* Convert routine to ANSI name */
3614     Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
3615                                           SystemRoutineName,
3616                                           TRUE);
3617     if (!NT_SUCCESS(Status)) return NULL;
3618 
3619     /* Lock the list */
3620     KeEnterCriticalRegion();
3621     ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);
3622 
3623     /* Loop the loaded module list */
3624     NextEntry = PsLoadedModuleList.Flink;
3625     while (NextEntry != &PsLoadedModuleList)
3626     {
3627         /* Get the entry */
3628         LdrEntry = CONTAINING_RECORD(NextEntry,
3629                                      LDR_DATA_TABLE_ENTRY,
3630                                      InLoadOrderLinks);
3631 
3632         /* Check if it's the kernel or HAL */
3633         if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
3634         {
3635             /* Found it */
3636             Found = TRUE;
3637             Modules++;
3638         }
3639         else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
3640         {
3641             /* Found it */
3642             Found = TRUE;
3643             Modules++;
3644         }
3645 
3646         /* Check if we found a valid binary */
3647         if (Found)
3648         {
3649             /* Find the procedure name */
3650             ProcAddress = RtlFindExportedRoutineByName(LdrEntry->DllBase,
3651                                                        AnsiRoutineName.Buffer);
3652 
3653             /* Break out if we found it or if we already tried both modules */
3654             if (ProcAddress) break;
3655             if (Modules == 2) break;
3656         }
3657 
3658         /* Keep looping */
3659         NextEntry = NextEntry->Flink;
3660     }
3661 
3662     /* Release the lock */
3663     ExReleaseResourceLite(&PsLoadedModuleResource);
3664     KeLeaveCriticalRegion();
3665 
3666     /* Free the string and return */
3667     RtlFreeAnsiString(&AnsiRoutineName);
3668     return ProcAddress;
3669 }
3670 
3671 /* EOF */
3672