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