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