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