Merge trunk r45185
[reactos.git] / reactos / ntoskrnl / mm / sysldr.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/mm/sysldr.c
5 * PURPOSE: Contains the Kernel Loader (SYSLDR) for loading PE files.
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 /* GCC's incompetence strikes again */
16 __inline
17 VOID
18 sprintf_nt(IN PCHAR Buffer,
19 IN PCHAR Format,
20 IN ...)
21 {
22 va_list ap;
23 va_start(ap, Format);
24 vsprintf(Buffer, Format, ap);
25 va_end(ap);
26 }
27
28 /* GLOBALS *******************************************************************/
29
30 LIST_ENTRY PsLoadedModuleList;
31 LIST_ENTRY MmLoadedUserImageList;
32 KSPIN_LOCK PsLoadedModuleSpinLock;
33 ERESOURCE PsLoadedModuleResource;
34 ULONG_PTR PsNtosImageBase;
35 KMUTANT MmSystemLoadLock;
36
37 PVOID MmUnloadedDrivers;
38 PVOID MmLastUnloadedDrivers;
39 PVOID MmTriageActionTaken;
40 PVOID KernelVerifier;
41
42 /* FUNCTIONS *****************************************************************/
43
44 PVOID
45 NTAPI
46 MiCacheImageSymbols(IN PVOID BaseAddress)
47 {
48 ULONG DebugSize;
49 PVOID DebugDirectory = NULL;
50 PAGED_CODE();
51
52 /* Make sure it's safe to access the image */
53 _SEH2_TRY
54 {
55 /* Get the debug directory */
56 DebugDirectory = RtlImageDirectoryEntryToData(BaseAddress,
57 TRUE,
58 IMAGE_DIRECTORY_ENTRY_DEBUG,
59 &DebugSize);
60 }
61 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
62 {
63 /* Nothing */
64 }
65 _SEH2_END;
66
67 /* Return the directory */
68 return DebugDirectory;
69 }
70
71 VOID
72 NTAPI
73 MiFreeBootDriverMemory(PVOID BaseAddress,
74 ULONG Length)
75 {
76 ULONG i;
77
78 /* Loop each page */
79 for (i = 0; i < PAGE_ROUND_UP(Length) / PAGE_SIZE; i++)
80 {
81 /* Free the page */
82 MmDeleteVirtualMapping(NULL,
83 (PVOID)((ULONG_PTR)BaseAddress + i * PAGE_SIZE),
84 TRUE,
85 NULL,
86 NULL);
87 }
88 }
89
90 NTSTATUS
91 NTAPI
92 MiLoadImageSection(IN OUT PVOID *SectionPtr,
93 OUT PVOID *ImageBase,
94 IN PUNICODE_STRING FileName,
95 IN BOOLEAN SessionLoad,
96 IN PLDR_DATA_TABLE_ENTRY LdrEntry)
97 {
98 PROS_SECTION_OBJECT Section = *SectionPtr;
99 NTSTATUS Status;
100 PEPROCESS Process;
101 PVOID Base = NULL;
102 SIZE_T ViewSize = 0;
103 KAPC_STATE ApcState;
104 LARGE_INTEGER SectionOffset = {{0, 0}};
105 BOOLEAN LoadSymbols = FALSE;
106 ULONG DriverSize;
107 PVOID DriverBase;
108 PAGED_CODE();
109
110 /* Detect session load */
111 if (SessionLoad)
112 {
113 /* Fail */
114 DPRINT1("Session loading not yet supported!\n");
115 while (TRUE);
116 }
117
118 /* Not session load, shouldn't have an entry */
119 ASSERT(LdrEntry == NULL);
120
121 /* Attach to the system process */
122 KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
123
124 /* Check if we need to load symbols */
125 if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD)
126 {
127 /* Yes we do */
128 LoadSymbols = TRUE;
129 NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
130 }
131
132 /* Map the driver */
133 Process = PsGetCurrentProcess();
134 Status = MmMapViewOfSection(Section,
135 Process,
136 &Base,
137 0,
138 0,
139 &SectionOffset,
140 &ViewSize,
141 ViewUnmap,
142 0,
143 PAGE_EXECUTE);
144
145 /* Re-enable the flag */
146 if (LoadSymbols) NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
147
148 /* Check if we failed with distinguished status code */
149 if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
150 {
151 /* Change it to something more generic */
152 Status = STATUS_INVALID_IMAGE_FORMAT;
153 }
154
155 /* Now check if we failed */
156 if (!NT_SUCCESS(Status))
157 {
158 /* Detach and return */
159 KeUnstackDetachProcess(&ApcState);
160 return Status;
161 }
162
163 /* Get the driver size */
164 DriverSize = Section->ImageSection->ImageSize;
165
166 /* Allocate a virtual section for the module */
167 DriverBase = MmAllocateSection(DriverSize, NULL);
168 *ImageBase = DriverBase;
169
170 /* Copy the image */
171 RtlCopyMemory(DriverBase, Base, DriverSize);
172
173 /* Now unmap the view */
174 Status = MmUnmapViewOfSection(Process, Base);
175 ASSERT(NT_SUCCESS(Status));
176
177 /* Detach and return status */
178 KeUnstackDetachProcess(&ApcState);
179 return Status;
180 }
181
182 NTSTATUS
183 NTAPI
184 MiDereferenceImports(IN PLOAD_IMPORTS ImportList)
185 {
186 SIZE_T i;
187
188 /* Check if there's no imports or if we're a boot driver */
189 if ((ImportList == (PVOID)-1) ||
190 (ImportList == (PVOID)-2) ||
191 (ImportList->Count == 0))
192 {
193 /* Then there's nothing to do */
194 return STATUS_SUCCESS;
195 }
196
197 /* Otherwise, FIXME */
198 DPRINT1("%u imports not dereferenced!\n", ImportList->Count);
199 for (i = 0; i < ImportList->Count; i++)
200 {
201 DPRINT1("%wZ <%wZ>\n", &ImportList->Entry[i]->FullDllName, &ImportList->Entry[i]->BaseDllName);
202 }
203 return STATUS_UNSUCCESSFUL;
204 }
205
206 VOID
207 NTAPI
208 MiClearImports(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
209 {
210 PAGED_CODE();
211
212 /* Check if there's no imports or we're a boot driver or only one entry */
213 if ((LdrEntry->LoadedImports == (PVOID)-1) ||
214 (LdrEntry->LoadedImports == (PVOID)-2) ||
215 ((ULONG_PTR)LdrEntry->LoadedImports & 1))
216 {
217 /* Nothing to do */
218 return;
219 }
220
221 /* Otherwise, free the import list */
222 ExFreePool(LdrEntry->LoadedImports);
223 }
224
225 PVOID
226 NTAPI
227 MiFindExportedRoutineByName(IN PVOID DllBase,
228 IN PANSI_STRING ExportName)
229 {
230 PULONG NameTable;
231 PUSHORT OrdinalTable;
232 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
233 LONG Low = 0, Mid = 0, High, Ret;
234 USHORT Ordinal;
235 PVOID Function;
236 ULONG ExportSize;
237 PULONG ExportTable;
238 PAGED_CODE();
239
240 /* Get the export directory */
241 ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
242 TRUE,
243 IMAGE_DIRECTORY_ENTRY_EXPORT,
244 &ExportSize);
245 if (!ExportDirectory) return NULL;
246
247 /* Setup name tables */
248 NameTable = (PULONG)((ULONG_PTR)DllBase +
249 ExportDirectory->AddressOfNames);
250 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
251 ExportDirectory->AddressOfNameOrdinals);
252
253 /* Do a binary search */
254 High = ExportDirectory->NumberOfNames - 1;
255 while (High >= Low)
256 {
257 /* Get new middle value */
258 Mid = (Low + High) >> 1;
259
260 /* Compare name */
261 Ret = strcmp(ExportName->Buffer, (PCHAR)DllBase + NameTable[Mid]);
262 if (Ret < 0)
263 {
264 /* Update high */
265 High = Mid - 1;
266 }
267 else if (Ret > 0)
268 {
269 /* Update low */
270 Low = Mid + 1;
271 }
272 else
273 {
274 /* We got it */
275 break;
276 }
277 }
278
279 /* Check if we couldn't find it */
280 if (High < Low) return NULL;
281
282 /* Otherwise, this is the ordinal */
283 Ordinal = OrdinalTable[Mid];
284
285 /* Resolve the address and write it */
286 ExportTable = (PULONG)((ULONG_PTR)DllBase +
287 ExportDirectory->AddressOfFunctions);
288 Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
289
290 /* We found it! */
291 ASSERT(!(Function > (PVOID)ExportDirectory) &&
292 (Function < (PVOID)((ULONG_PTR)ExportDirectory + ExportSize)));
293 return Function;
294 }
295
296 PVOID
297 NTAPI
298 MiLocateExportName(IN PVOID DllBase,
299 IN PCHAR ExportName)
300 {
301 PULONG NameTable;
302 PUSHORT OrdinalTable;
303 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
304 LONG Low = 0, Mid = 0, High, Ret;
305 USHORT Ordinal;
306 PVOID Function;
307 ULONG ExportSize;
308 PULONG ExportTable;
309 PAGED_CODE();
310
311 /* Get the export directory */
312 ExportDirectory = RtlImageDirectoryEntryToData(DllBase,
313 TRUE,
314 IMAGE_DIRECTORY_ENTRY_EXPORT,
315 &ExportSize);
316 if (!ExportDirectory) return NULL;
317
318 /* Setup name tables */
319 NameTable = (PULONG)((ULONG_PTR)DllBase +
320 ExportDirectory->AddressOfNames);
321 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
322 ExportDirectory->AddressOfNameOrdinals);
323
324 /* Do a binary search */
325 High = ExportDirectory->NumberOfNames - 1;
326 while (High >= Low)
327 {
328 /* Get new middle value */
329 Mid = (Low + High) >> 1;
330
331 /* Compare name */
332 Ret = strcmp(ExportName, (PCHAR)DllBase + NameTable[Mid]);
333 if (Ret < 0)
334 {
335 /* Update high */
336 High = Mid - 1;
337 }
338 else if (Ret > 0)
339 {
340 /* Update low */
341 Low = Mid + 1;
342 }
343 else
344 {
345 /* We got it */
346 break;
347 }
348 }
349
350 /* Check if we couldn't find it */
351 if (High < Low) return NULL;
352
353 /* Otherwise, this is the ordinal */
354 Ordinal = OrdinalTable[Mid];
355
356 /* Resolve the address and write it */
357 ExportTable = (PULONG)((ULONG_PTR)DllBase +
358 ExportDirectory->AddressOfFunctions);
359 Function = (PVOID)((ULONG_PTR)DllBase + ExportTable[Ordinal]);
360
361 /* Check if the function is actually a forwarder */
362 if (((ULONG_PTR)Function > (ULONG_PTR)ExportDirectory) &&
363 ((ULONG_PTR)Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
364 {
365 /* It is, fail */
366 return NULL;
367 }
368
369 /* We found it */
370 return Function;
371 }
372
373 NTSTATUS
374 NTAPI
375 MmCallDllInitialize(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
376 IN PLIST_ENTRY ListHead)
377 {
378 UNICODE_STRING ServicesKeyName = RTL_CONSTANT_STRING(
379 L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
380 PMM_DLL_INITIALIZE DllInit;
381 UNICODE_STRING RegPath, ImportName;
382 NTSTATUS Status;
383
384 /* Try to see if the image exports a DllInitialize routine */
385 DllInit = (PMM_DLL_INITIALIZE)MiLocateExportName(LdrEntry->DllBase,
386 "DllInitialize");
387 if (!DllInit) return STATUS_SUCCESS;
388
389 /* Do a temporary copy of BaseDllName called ImportName
390 * because we'll alter the length of the string
391 */
392 ImportName.Length = LdrEntry->BaseDllName.Length;
393 ImportName.MaximumLength = LdrEntry->BaseDllName.MaximumLength;
394 ImportName.Buffer = LdrEntry->BaseDllName.Buffer;
395
396 /* Obtain the path to this dll's service in the registry */
397 RegPath.MaximumLength = ServicesKeyName.Length +
398 ImportName.Length + sizeof(UNICODE_NULL);
399 RegPath.Buffer = ExAllocatePoolWithTag(NonPagedPool,
400 RegPath.MaximumLength,
401 TAG_LDR_WSTR);
402
403 /* Check if this allocation was unsuccessful */
404 if (!RegPath.Buffer) return STATUS_INSUFFICIENT_RESOURCES;
405
406 /* Build and append the service name itself */
407 RegPath.Length = ServicesKeyName.Length;
408 RtlCopyMemory(RegPath.Buffer,
409 ServicesKeyName.Buffer,
410 ServicesKeyName.Length);
411
412 /* Check if there is a dot in the filename */
413 if (wcschr(ImportName.Buffer, L'.'))
414 {
415 /* Remove the extension */
416 ImportName.Length = (wcschr(ImportName.Buffer, L'.') -
417 ImportName.Buffer) * sizeof(WCHAR);
418 }
419
420 /* Append service name (the basename without extension) */
421 RtlAppendUnicodeStringToString(&RegPath, &ImportName);
422
423 /* Now call the DllInit func */
424 DPRINT("Calling DllInit(%wZ)\n", &RegPath);
425 Status = DllInit(&RegPath);
426
427 /* Clean up */
428 ExFreePool(RegPath.Buffer);
429
430 /* Return status value which DllInitialize returned */
431 return Status;
432 }
433
434 VOID
435 NTAPI
436 MiProcessLoaderEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
437 IN BOOLEAN Insert)
438 {
439 KIRQL OldIrql;
440
441 /* Acquire module list lock */
442 KeEnterCriticalRegion();
443 ExAcquireResourceExclusiveLite(&PsLoadedModuleResource, TRUE);
444
445 /* Acquire the spinlock too as we will insert or remove the entry */
446 OldIrql = KeAcquireSpinLockRaiseToSynch(&PsLoadedModuleSpinLock);
447
448 /* Insert or remove from the list */
449 Insert ? InsertTailList(&PsLoadedModuleList, &LdrEntry->InLoadOrderLinks) :
450 RemoveEntryList(&LdrEntry->InLoadOrderLinks);
451
452 /* Release locks */
453 KeReleaseSpinLock(&PsLoadedModuleSpinLock, OldIrql);
454 ExReleaseResourceLite(&PsLoadedModuleResource);
455 KeLeaveCriticalRegion();
456 }
457
458 VOID
459 NTAPI
460 MiUpdateThunks(IN PLOADER_PARAMETER_BLOCK LoaderBlock,
461 IN PVOID OldBase,
462 IN PVOID NewBase,
463 IN ULONG Size)
464 {
465 ULONG_PTR OldBaseTop, Delta;
466 PLDR_DATA_TABLE_ENTRY LdrEntry;
467 PLIST_ENTRY NextEntry;
468 ULONG ImportSize;
469 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
470 PULONG ImageThunk;
471
472 /* Calculate the top and delta */
473 OldBaseTop = (ULONG_PTR)OldBase + Size - 1;
474 Delta = (ULONG_PTR)NewBase - (ULONG_PTR)OldBase;
475
476 /* Loop the loader block */
477 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
478 NextEntry != &LoaderBlock->LoadOrderListHead;
479 NextEntry = NextEntry->Flink)
480 {
481 /* Get the loader entry */
482 LdrEntry = CONTAINING_RECORD(NextEntry,
483 LDR_DATA_TABLE_ENTRY,
484 InLoadOrderLinks);
485
486 /* Get the import table */
487 ImportDescriptor = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
488 TRUE,
489 IMAGE_DIRECTORY_ENTRY_IMPORT,
490 &ImportSize);
491 if (!ImportDescriptor) continue;
492
493 /* Make sure we have an IAT */
494 DPRINT("[Mm0]: Updating thunks in: %wZ\n", &LdrEntry->BaseDllName);
495 while ((ImportDescriptor->Name) &&
496 (ImportDescriptor->OriginalFirstThunk))
497 {
498 /* Get the image thunk */
499 ImageThunk = (PVOID)((ULONG_PTR)LdrEntry->DllBase +
500 ImportDescriptor->FirstThunk);
501 while (*ImageThunk)
502 {
503 /* Check if it's within this module */
504 if ((*ImageThunk >= (ULONG_PTR)OldBase) && (*ImageThunk <= OldBaseTop))
505 {
506 /* Relocate it */
507 DPRINT("[Mm0]: Updating IAT at: %p. Old Entry: %p. New Entry: %p.\n",
508 ImageThunk, *ImageThunk, *ImageThunk + Delta);
509 *ImageThunk += Delta;
510 }
511
512 /* Go to the next thunk */
513 ImageThunk++;
514 }
515
516 /* Go to the next import */
517 ImportDescriptor++;
518 }
519 }
520 }
521
522 NTSTATUS
523 NTAPI
524 MiSnapThunk(IN PVOID DllBase,
525 IN PVOID ImageBase,
526 IN PIMAGE_THUNK_DATA Name,
527 IN PIMAGE_THUNK_DATA Address,
528 IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
529 IN ULONG ExportSize,
530 IN BOOLEAN SnapForwarder,
531 OUT PCHAR *MissingApi)
532 {
533 BOOLEAN IsOrdinal;
534 USHORT Ordinal;
535 PULONG NameTable;
536 PUSHORT OrdinalTable;
537 PIMAGE_IMPORT_BY_NAME NameImport;
538 USHORT Hint;
539 ULONG Low = 0, Mid = 0, High;
540 LONG Ret;
541 NTSTATUS Status;
542 PCHAR MissingForwarder;
543 CHAR NameBuffer[MAXIMUM_FILENAME_LENGTH];
544 PULONG ExportTable;
545 ANSI_STRING DllName;
546 UNICODE_STRING ForwarderName;
547 PLIST_ENTRY NextEntry;
548 PLDR_DATA_TABLE_ENTRY LdrEntry;
549 ULONG ForwardExportSize;
550 PIMAGE_EXPORT_DIRECTORY ForwardExportDirectory;
551 PIMAGE_IMPORT_BY_NAME ForwardName;
552 ULONG ForwardLength;
553 IMAGE_THUNK_DATA ForwardThunk;
554 PAGED_CODE();
555
556 /* Check if this is an ordinal */
557 IsOrdinal = IMAGE_SNAP_BY_ORDINAL(Name->u1.Ordinal);
558 if ((IsOrdinal) && !(SnapForwarder))
559 {
560 /* Get the ordinal number and set it as missing */
561 Ordinal = (USHORT)(IMAGE_ORDINAL(Name->u1.Ordinal) -
562 ExportDirectory->Base);
563 *MissingApi = (PCHAR)(ULONG_PTR)Ordinal;
564 }
565 else
566 {
567 /* Get the VA if we don't have to snap */
568 if (!SnapForwarder) Name->u1.AddressOfData += (ULONG_PTR)ImageBase;
569 NameImport = (PIMAGE_IMPORT_BY_NAME)Name->u1.AddressOfData;
570
571 /* Copy the procedure name */
572 strncpy(*MissingApi,
573 (PCHAR)&NameImport->Name[0],
574 MAXIMUM_FILENAME_LENGTH - 1);
575
576 /* Setup name tables */
577 DPRINT("Import name: %s\n", NameImport->Name);
578 NameTable = (PULONG)((ULONG_PTR)DllBase +
579 ExportDirectory->AddressOfNames);
580 OrdinalTable = (PUSHORT)((ULONG_PTR)DllBase +
581 ExportDirectory->AddressOfNameOrdinals);
582
583 /* Get the hint and check if it's valid */
584 Hint = NameImport->Hint;
585 if ((Hint < ExportDirectory->NumberOfNames) &&
586 !(strcmp((PCHAR) NameImport->Name, (PCHAR)DllBase + NameTable[Hint])))
587 {
588 /* We have a match, get the ordinal number from here */
589 Ordinal = OrdinalTable[Hint];
590 }
591 else
592 {
593 /* Do a binary search */
594 High = ExportDirectory->NumberOfNames - 1;
595 while (High >= Low)
596 {
597 /* Get new middle value */
598 Mid = (Low + High) >> 1;
599
600 /* Compare name */
601 Ret = strcmp((PCHAR)NameImport->Name, (PCHAR)DllBase + NameTable[Mid]);
602 if (Ret < 0)
603 {
604 /* Update high */
605 High = Mid - 1;
606 }
607 else if (Ret > 0)
608 {
609 /* Update low */
610 Low = Mid + 1;
611 }
612 else
613 {
614 /* We got it */
615 break;
616 }
617 }
618
619 /* Check if we couldn't find it */
620 if (High < Low) return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
621
622 /* Otherwise, this is the ordinal */
623 Ordinal = OrdinalTable[Mid];
624 }
625 }
626
627 /* Check if the ordinal is invalid */
628 if (Ordinal >= ExportDirectory->NumberOfFunctions)
629 {
630 /* Fail */
631 Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
632 }
633 else
634 {
635 /* In case the forwarder is missing */
636 MissingForwarder = NameBuffer;
637
638 /* Resolve the address and write it */
639 ExportTable = (PULONG)((ULONG_PTR)DllBase +
640 ExportDirectory->AddressOfFunctions);
641 Address->u1.Function = (ULONG_PTR)DllBase + ExportTable[Ordinal];
642
643 /* Assume success from now on */
644 Status = STATUS_SUCCESS;
645
646 /* Check if the function is actually a forwarder */
647 if ((Address->u1.Function > (ULONG_PTR)ExportDirectory) &&
648 (Address->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)))
649 {
650 /* Now assume failure in case the forwarder doesn't exist */
651 Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
652
653 /* Build the forwarder name */
654 DllName.Buffer = (PCHAR)Address->u1.Function;
655 DllName.Length = strchr(DllName.Buffer, '.') -
656 DllName.Buffer +
657 sizeof(ANSI_NULL);
658 DllName.MaximumLength = DllName.Length;
659
660 /* Convert it */
661 if (!NT_SUCCESS(RtlAnsiStringToUnicodeString(&ForwarderName,
662 &DllName,
663 TRUE)))
664 {
665 /* We failed, just return an error */
666 return Status;
667 }
668
669 /* Loop the module list */
670 NextEntry = PsLoadedModuleList.Flink;
671 while (NextEntry != &PsLoadedModuleList)
672 {
673 /* Get the loader entry */
674 LdrEntry = CONTAINING_RECORD(NextEntry,
675 LDR_DATA_TABLE_ENTRY,
676 InLoadOrderLinks);
677
678 /* Check if it matches */
679 if (RtlPrefixString((PSTRING)&ForwarderName,
680 (PSTRING)&LdrEntry->BaseDllName,
681 TRUE))
682 {
683 /* Get the forwarder export directory */
684 ForwardExportDirectory =
685 RtlImageDirectoryEntryToData(LdrEntry->DllBase,
686 TRUE,
687 IMAGE_DIRECTORY_ENTRY_EXPORT,
688 &ForwardExportSize);
689 if (!ForwardExportDirectory) break;
690
691 /* Allocate a name entry */
692 ForwardLength = strlen(DllName.Buffer + DllName.Length) +
693 sizeof(ANSI_NULL);
694 ForwardName = ExAllocatePoolWithTag(PagedPool,
695 sizeof(*ForwardName) +
696 ForwardLength,
697 TAG_LDR_WSTR);
698 if (!ForwardName) break;
699
700 /* Copy the data */
701 RtlCopyMemory(&ForwardName->Name[0],
702 DllName.Buffer + DllName.Length,
703 ForwardLength);
704 ForwardName->Hint = 0;
705
706 /* Set the new address */
707 ForwardThunk.u1.AddressOfData = (ULONG_PTR)ForwardName;
708
709 /* Snap the forwarder */
710 Status = MiSnapThunk(LdrEntry->DllBase,
711 ImageBase,
712 &ForwardThunk,
713 &ForwardThunk,
714 ForwardExportDirectory,
715 ForwardExportSize,
716 TRUE,
717 &MissingForwarder);
718
719 /* Free the forwarder name and set the thunk */
720 ExFreePoolWithTag(ForwardName, TAG_LDR_WSTR);
721 Address->u1 = ForwardThunk.u1;
722 break;
723 }
724
725 /* Go to the next entry */
726 NextEntry = NextEntry->Flink;
727 }
728
729 /* Free the name */
730 RtlFreeUnicodeString(&ForwarderName);
731 }
732 }
733
734 /* Return status */
735 return Status;
736 }
737
738 NTSTATUS
739 NTAPI
740 MmUnloadSystemImage(IN PVOID ImageHandle)
741 {
742 PLDR_DATA_TABLE_ENTRY LdrEntry = ImageHandle;
743 PVOID BaseAddress = LdrEntry->DllBase;
744 NTSTATUS Status;
745 STRING TempName;
746 BOOLEAN HadEntry = FALSE;
747
748 /* Acquire the loader lock */
749 KeEnterCriticalRegion();
750 KeWaitForSingleObject(&MmSystemLoadLock,
751 WrVirtualMemory,
752 KernelMode,
753 FALSE,
754 NULL);
755
756 /* Check if this driver was loaded at boot and didn't get imports parsed */
757 if (LdrEntry->LoadedImports == (PVOID)-1) goto Done;
758
759 /* We should still be alive */
760 ASSERT(LdrEntry->LoadCount != 0);
761 LdrEntry->LoadCount--;
762
763 /* Check if we're still loaded */
764 if (LdrEntry->LoadCount) goto Done;
765
766 /* We should cleanup... are symbols loaded */
767 if (LdrEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED)
768 {
769 /* Create the ANSI name */
770 Status = RtlUnicodeStringToAnsiString(&TempName,
771 &LdrEntry->BaseDllName,
772 TRUE);
773 if (NT_SUCCESS(Status))
774 {
775 /* Unload the symbols */
776 DbgUnLoadImageSymbols(&TempName,
777 BaseAddress,
778 (ULONG_PTR)ZwCurrentProcess());
779 RtlFreeAnsiString(&TempName);
780 }
781 }
782
783 /* Free the driver */
784 MmFreeSection(LdrEntry->DllBase);
785
786 /* Check if we're linked in */
787 if (LdrEntry->InLoadOrderLinks.Flink)
788 {
789 /* Remove us */
790 MiProcessLoaderEntry(LdrEntry, FALSE);
791 HadEntry = TRUE;
792 }
793
794 /* Dereference and clear the imports */
795 MiDereferenceImports(LdrEntry->LoadedImports);
796 MiClearImports(LdrEntry);
797
798 /* Check if the entry needs to go away */
799 if (HadEntry)
800 {
801 /* Check if it had a name */
802 if (LdrEntry->FullDllName.Buffer)
803 {
804 /* Free it */
805 ExFreePool(LdrEntry->FullDllName.Buffer);
806 }
807
808 /* Check if we had a section */
809 if (LdrEntry->SectionPointer)
810 {
811 /* Dereference it */
812 ObDereferenceObject(LdrEntry->SectionPointer);
813 }
814
815 /* Free the entry */
816 ExFreePool(LdrEntry);
817 }
818
819 /* Release the system lock and return */
820 Done:
821 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
822 KeLeaveCriticalRegion();
823 return STATUS_SUCCESS;
824 }
825
826 NTSTATUS
827 NTAPI
828 MiResolveImageReferences(IN PVOID ImageBase,
829 IN PUNICODE_STRING ImageFileDirectory,
830 IN PUNICODE_STRING NamePrefix OPTIONAL,
831 OUT PCHAR *MissingApi,
832 OUT PWCHAR *MissingDriver,
833 OUT PLOAD_IMPORTS *LoadImports)
834 {
835 PCHAR MissingApiBuffer = *MissingApi, ImportName;
836 PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor, CurrentImport;
837 ULONG ImportSize, ImportCount = 0, LoadedImportsSize, ExportSize;
838 PLOAD_IMPORTS LoadedImports, NewImports;
839 ULONG GdiLink, NormalLink, i;
840 BOOLEAN ReferenceNeeded, Loaded;
841 ANSI_STRING TempString;
842 UNICODE_STRING NameString, DllName;
843 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL, DllEntry, ImportEntry = NULL;
844 PVOID ImportBase, DllBase;
845 PLIST_ENTRY NextEntry;
846 PIMAGE_EXPORT_DIRECTORY ExportDirectory;
847 NTSTATUS Status;
848 PIMAGE_THUNK_DATA OrigThunk, FirstThunk;
849 PAGED_CODE();
850 DPRINT("%s - ImageBase: %p. ImageFileDirectory: %wZ\n",
851 __FUNCTION__, ImageBase, ImageFileDirectory);
852
853 /* Assume no imports */
854 *LoadImports = (PVOID)-2;
855
856 /* Get the import descriptor */
857 ImportDescriptor = RtlImageDirectoryEntryToData(ImageBase,
858 TRUE,
859 IMAGE_DIRECTORY_ENTRY_IMPORT,
860 &ImportSize);
861 if (!ImportDescriptor) return STATUS_SUCCESS;
862
863 /* Loop all imports to count them */
864 for (CurrentImport = ImportDescriptor;
865 (CurrentImport->Name) && (CurrentImport->OriginalFirstThunk);
866 CurrentImport++)
867 {
868 /* One more */
869 ImportCount++;
870 }
871
872 /* Make sure we have non-zero imports */
873 if (ImportCount)
874 {
875 /* Calculate and allocate the list we'll need */
876 LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
877 LoadedImports = ExAllocatePoolWithTag(PagedPool,
878 LoadedImportsSize,
879 TAG_LDR_WSTR);
880 if (LoadedImports)
881 {
882 /* Zero it */
883 RtlZeroMemory(LoadedImports, LoadedImportsSize);
884 }
885 }
886 else
887 {
888 /* No table */
889 LoadedImports = NULL;
890 }
891
892 /* Reset the import count and loop descriptors again */
893 ImportCount = GdiLink = NormalLink = 0;
894 while ((ImportDescriptor->Name) && (ImportDescriptor->OriginalFirstThunk))
895 {
896 /* Get the name */
897 ImportName = (PCHAR)((ULONG_PTR)ImageBase + ImportDescriptor->Name);
898
899 /* Check if this is a GDI driver */
900 GdiLink = GdiLink |
901 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
902
903 /* We can also allow dxapi */
904 NormalLink = NormalLink |
905 ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
906 (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)));
907
908 /* Check if this is a valid GDI driver */
909 if ((GdiLink) && (NormalLink))
910 {
911 /* It's not, it's importing stuff it shouldn't be! */
912 MiDereferenceImports(LoadedImports);
913 if (LoadedImports) ExFreePool(LoadedImports);
914 return STATUS_PROCEDURE_NOT_FOUND;
915 }
916
917 /* Check if this is a "core" import, which doesn't get referenced */
918 if (!(_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
919 !(_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
920 !(_strnicmp(ImportName, "hal", sizeof("hal") - 1)))
921 {
922 /* Don't reference this */
923 ReferenceNeeded = FALSE;
924 }
925 else
926 {
927 /* Reference these modules */
928 ReferenceNeeded = TRUE;
929 }
930
931 /* Now setup a unicode string for the import */
932 RtlInitAnsiString(&TempString, ImportName);
933 Status = RtlAnsiStringToUnicodeString(&NameString, &TempString, TRUE);
934 if (!NT_SUCCESS(Status))
935 {
936 /* Failed */
937 MiDereferenceImports(LoadedImports);
938 if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
939 return Status;
940 }
941
942 /* We don't support name prefixes yet */
943 if (NamePrefix) DPRINT1("Name Prefix not yet supported!\n");
944
945 /* Remember that we haven't loaded the import at this point */
946 CheckDllState:
947 Loaded = FALSE;
948 ImportBase = NULL;
949
950 /* Loop the driver list */
951 NextEntry = PsLoadedModuleList.Flink;
952 while (NextEntry != &PsLoadedModuleList)
953 {
954 /* Get the loader entry and compare the name */
955 LdrEntry = CONTAINING_RECORD(NextEntry,
956 LDR_DATA_TABLE_ENTRY,
957 InLoadOrderLinks);
958 if (RtlEqualUnicodeString(&NameString,
959 &LdrEntry->BaseDllName,
960 TRUE))
961 {
962 /* Get the base address */
963 ImportBase = LdrEntry->DllBase;
964
965 /* Check if we haven't loaded yet, and we need references */
966 if (!(Loaded) && (ReferenceNeeded))
967 {
968 /* Make sure we're not already loading */
969 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
970 {
971 /* Increase the load count */
972 LdrEntry->LoadCount++;
973 }
974 }
975
976 /* Done, break out */
977 break;
978 }
979
980 /* Go to the next entry */
981 NextEntry = NextEntry->Flink;
982 }
983
984 /* Check if we haven't loaded the import yet */
985 if (!ImportBase)
986 {
987 /* Setup the import DLL name */
988 DllName.MaximumLength = NameString.Length +
989 ImageFileDirectory->Length +
990 sizeof(UNICODE_NULL);
991 DllName.Buffer = ExAllocatePoolWithTag(NonPagedPool,
992 DllName.MaximumLength,
993 TAG_LDR_WSTR);
994 if (DllName.Buffer)
995 {
996 /* Setup the base length and copy it */
997 DllName.Length = ImageFileDirectory->Length;
998 RtlCopyMemory(DllName.Buffer,
999 ImageFileDirectory->Buffer,
1000 ImageFileDirectory->Length);
1001
1002 /* Now add the import name and null-terminate it */
1003 RtlAppendStringToString((PSTRING)&DllName,
1004 (PSTRING)&NameString);
1005 DllName.Buffer[(DllName.MaximumLength - 1) / sizeof(WCHAR)] = UNICODE_NULL;
1006
1007 /* Load the image */
1008 Status = MmLoadSystemImage(&DllName,
1009 NamePrefix,
1010 NULL,
1011 0,
1012 (PVOID)&DllEntry,
1013 &DllBase);
1014 if (NT_SUCCESS(Status))
1015 {
1016 /* We can free the DLL Name */
1017 ExFreePool(DllName.Buffer);
1018 }
1019 else
1020 {
1021 /* Fill out the information for the error */
1022 *MissingDriver = DllName.Buffer;
1023 *(PULONG)MissingDriver |= 1;
1024 *MissingApi = NULL;
1025 }
1026 }
1027 else
1028 {
1029 /* We're out of resources */
1030 Status = STATUS_INSUFFICIENT_RESOURCES;
1031 }
1032
1033 /* Check if we're OK until now */
1034 if (NT_SUCCESS(Status))
1035 {
1036 /* We're now loaded */
1037 Loaded = TRUE;
1038
1039 /* Sanity check */
1040 ASSERT(DllBase = DllEntry->DllBase);
1041
1042 /* Call the initialization routines */
1043 Status = MmCallDllInitialize(DllEntry, &PsLoadedModuleList);
1044 if (!NT_SUCCESS(Status))
1045 {
1046 /* We failed, unload the image */
1047 MmUnloadSystemImage(DllEntry);
1048 while (TRUE);
1049 Loaded = FALSE;
1050 }
1051 }
1052
1053 /* Check if we failed by here */
1054 if (!NT_SUCCESS(Status))
1055 {
1056 /* Cleanup and return */
1057 RtlFreeUnicodeString(&NameString);
1058 MiDereferenceImports(LoadedImports);
1059 if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1060 return Status;
1061 }
1062
1063 /* Loop again to make sure that everything is OK */
1064 goto CheckDllState;
1065 }
1066
1067 /* Check if we're support to reference this import */
1068 if ((ReferenceNeeded) && (LoadedImports))
1069 {
1070 /* Make sure we're not already loading */
1071 if (!(LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS))
1072 {
1073 /* Add the entry */
1074 LoadedImports->Entry[LoadedImports->Count] = LdrEntry;
1075 LoadedImports->Count++;
1076 }
1077 }
1078
1079 /* Free the import name */
1080 RtlFreeUnicodeString(&NameString);
1081
1082 /* Set the missing driver name and get the export directory */
1083 *MissingDriver = LdrEntry->BaseDllName.Buffer;
1084 ExportDirectory = RtlImageDirectoryEntryToData(ImportBase,
1085 TRUE,
1086 IMAGE_DIRECTORY_ENTRY_EXPORT,
1087 &ExportSize);
1088 if (!ExportDirectory)
1089 {
1090 /* Cleanup and return */
1091 MiDereferenceImports(LoadedImports);
1092 if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1093 return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
1094 }
1095
1096 /* Make sure we have an IAT */
1097 if (ImportDescriptor->OriginalFirstThunk)
1098 {
1099 /* Get the first thunks */
1100 OrigThunk = (PVOID)((ULONG_PTR)ImageBase +
1101 ImportDescriptor->OriginalFirstThunk);
1102 FirstThunk = (PVOID)((ULONG_PTR)ImageBase +
1103 ImportDescriptor->FirstThunk);
1104
1105 /* Loop the IAT */
1106 while (OrigThunk->u1.AddressOfData)
1107 {
1108 /* Snap thunk */
1109 Status = MiSnapThunk(ImportBase,
1110 ImageBase,
1111 OrigThunk++,
1112 FirstThunk++,
1113 ExportDirectory,
1114 ExportSize,
1115 FALSE,
1116 MissingApi);
1117 if (!NT_SUCCESS(Status))
1118 {
1119 /* Cleanup and return */
1120 MiDereferenceImports(LoadedImports);
1121 if (LoadedImports) ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1122 return Status;
1123 }
1124
1125 /* Reset the buffer */
1126 *MissingApi = MissingApiBuffer;
1127 }
1128 }
1129
1130 /* Go to the next import */
1131 ImportDescriptor++;
1132 }
1133
1134 /* Check if we have an import list */
1135 if (LoadedImports)
1136 {
1137 /* Reset the count again, and loop entries*/
1138 ImportCount = 0;
1139 for (i = 0; i < LoadedImports->Count; i++)
1140 {
1141 if (LoadedImports->Entry[i])
1142 {
1143 /* Got an entry, OR it with 1 in case it's the single entry */
1144 ImportEntry = (PVOID)((ULONG_PTR)LoadedImports->Entry[i] | 1);
1145 ImportCount++;
1146 }
1147 }
1148
1149 /* Check if we had no imports */
1150 if (!ImportCount)
1151 {
1152 /* Free the list and set it to no imports */
1153 ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1154 LoadedImports = (PVOID)-2;
1155 }
1156 else if (ImportCount == 1)
1157 {
1158 /* Just one entry, we can free the table and only use our entry */
1159 ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1160 LoadedImports = (PLOAD_IMPORTS)ImportEntry;
1161 }
1162 else if (ImportCount != LoadedImports->Count)
1163 {
1164 /* Allocate a new list */
1165 LoadedImportsSize = ImportCount * sizeof(PVOID) + sizeof(SIZE_T);
1166 NewImports = ExAllocatePoolWithTag(PagedPool,
1167 LoadedImportsSize,
1168 TAG_LDR_WSTR);
1169 if (NewImports)
1170 {
1171 /* Set count */
1172 NewImports->Count = 0;
1173
1174 /* Loop all the imports */
1175 for (i = 0; i < LoadedImports->Count; i++)
1176 {
1177 /* Make sure it's valid */
1178 if (LoadedImports->Entry[i])
1179 {
1180 /* Copy it */
1181 NewImports->Entry[NewImports->Count] = LoadedImports->Entry[i];
1182 NewImports->Count++;
1183 }
1184 }
1185
1186 /* Free the old copy */
1187 ExFreePoolWithTag(LoadedImports, TAG_LDR_WSTR);
1188 LoadedImports = NewImports;
1189 }
1190 }
1191
1192 /* Return the list */
1193 *LoadImports = LoadedImports;
1194 }
1195
1196 /* Return success */
1197 return STATUS_SUCCESS;
1198 }
1199
1200 VOID
1201 NTAPI
1202 MiReloadBootLoadedDrivers(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1203 {
1204 PLIST_ENTRY NextEntry;
1205 ULONG i = 0;
1206 PIMAGE_NT_HEADERS NtHeader;
1207 PLDR_DATA_TABLE_ENTRY LdrEntry;
1208 PIMAGE_FILE_HEADER FileHeader;
1209 BOOLEAN ValidRelocs = FALSE;
1210 PIMAGE_DATA_DIRECTORY DataDirectory;
1211 PVOID DllBase, NewImageAddress;
1212 NTSTATUS Status;
1213
1214 /* Sanity check */
1215 ASSERT(ExpInitializationPhase == 0);
1216
1217 /* Loop driver list */
1218 for (NextEntry = LoaderBlock->LoadOrderListHead.Flink;
1219 NextEntry != &LoaderBlock->LoadOrderListHead;
1220 NextEntry = NextEntry->Flink)
1221 {
1222 /* Get the loader entry and NT header */
1223 LdrEntry = CONTAINING_RECORD(NextEntry,
1224 LDR_DATA_TABLE_ENTRY,
1225 InLoadOrderLinks);
1226 NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
1227
1228 /* Debug info */
1229 DPRINT("[Mm0]: Driver at: %p ending at: %p for module: %wZ\n",
1230 LdrEntry->DllBase,
1231 (ULONG_PTR)LdrEntry->DllBase + LdrEntry->SizeOfImage,
1232 &LdrEntry->FullDllName);
1233
1234 /* Skip kernel and HAL */
1235 /* ROS HACK: Skip BOOTVID/KDCOM too */
1236 i++;
1237 if (i <= 4) continue;
1238
1239 /* Skip non-drivers */
1240 if (!NtHeader) continue;
1241
1242 /* Get the file header and check if we can relocate */
1243 FileHeader = &NtHeader->FileHeader;
1244 if ( !(FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) &&
1245 (NtHeader->OptionalHeader.NumberOfRvaAndSizes >
1246 IMAGE_DIRECTORY_ENTRY_BASERELOC) )
1247 {
1248 /* Everything made sense until now, check the relocation section too */
1249 DataDirectory = &NtHeader->OptionalHeader.
1250 DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
1251 if (DataDirectory->VirtualAddress)
1252 {
1253 /* Make sure the size is valid */
1254 if ((DataDirectory->VirtualAddress + DataDirectory->Size) >
1255 LdrEntry->SizeOfImage)
1256 {
1257 /* They're not, skip */
1258 continue;
1259 }
1260
1261 /* We have relocations */
1262 ValidRelocs = TRUE;
1263 }
1264 }
1265
1266 /* Remember the original address */
1267 DllBase = LdrEntry->DllBase;
1268
1269 /* Allocate a virtual section for the module */
1270 NewImageAddress = MmAllocateSection(LdrEntry->SizeOfImage, NULL);
1271 if (!NewImageAddress)
1272 {
1273 /* Shouldn't happen */
1274 DPRINT1("[Mm0]: Couldn't allocate driver section!\n");
1275 while (TRUE);
1276 }
1277
1278 DPRINT("[Mm0]: Copying from: %p to: %p\n", DllBase, NewImageAddress);
1279
1280 /* Now copy the entire driver over */
1281 RtlCopyMemory(NewImageAddress, DllBase, LdrEntry->SizeOfImage);
1282
1283 /* Sanity check */
1284 ASSERT(*(PULONG)NewImageAddress == *(PULONG)DllBase);
1285
1286 /* Set the image base to the address where the loader put it */
1287 NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
1288 NtHeader = RtlImageNtHeader(NewImageAddress);
1289 NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)DllBase;
1290
1291 /* Check if we had relocations */
1292 if (ValidRelocs)
1293 {
1294 /* Relocate the image */
1295 Status = LdrRelocateImageWithBias(NewImageAddress,
1296 0,
1297 "SYSLDR",
1298 STATUS_SUCCESS,
1299 STATUS_CONFLICTING_ADDRESSES,
1300 STATUS_INVALID_IMAGE_FORMAT);
1301 if (!NT_SUCCESS(Status))
1302 {
1303 /* This shouldn't happen */
1304 DPRINT1("Relocations failed!\n");
1305 while (TRUE);
1306 }
1307 }
1308
1309 /* Update the loader entry */
1310 LdrEntry->DllBase = NewImageAddress;
1311
1312 /* Update the thunks */
1313 DPRINT("[Mm0]: Updating thunks to: %wZ\n", &LdrEntry->BaseDllName);
1314 MiUpdateThunks(LoaderBlock,
1315 DllBase,
1316 NewImageAddress,
1317 LdrEntry->SizeOfImage);
1318
1319 /* Update the loader entry */
1320 LdrEntry->Flags |= LDRP_SYSTEM_MAPPED;
1321 LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)NewImageAddress +
1322 NtHeader->OptionalHeader.AddressOfEntryPoint);
1323 LdrEntry->SizeOfImage = LdrEntry->SizeOfImage;
1324
1325 /* Free the old copy */
1326 MiFreeBootDriverMemory(DllBase, LdrEntry->SizeOfImage);
1327 }
1328 }
1329
1330 BOOLEAN
1331 NTAPI
1332 MiInitializeLoadedModuleList(IN PLOADER_PARAMETER_BLOCK LoaderBlock)
1333 {
1334 PLDR_DATA_TABLE_ENTRY LdrEntry, NewEntry;
1335 PLIST_ENTRY ListHead, NextEntry;
1336 ULONG EntrySize;
1337
1338 /* Setup the loaded module list and locks */
1339 ExInitializeResourceLite(&PsLoadedModuleResource);
1340 KeInitializeSpinLock(&PsLoadedModuleSpinLock);
1341 InitializeListHead(&PsLoadedModuleList);
1342
1343 /* Get loop variables and the kernel entry */
1344 ListHead = &LoaderBlock->LoadOrderListHead;
1345 NextEntry = ListHead->Flink;
1346 LdrEntry = CONTAINING_RECORD(NextEntry,
1347 LDR_DATA_TABLE_ENTRY,
1348 InLoadOrderLinks);
1349 PsNtosImageBase = (ULONG_PTR)LdrEntry->DllBase;
1350
1351 /* Loop the loader block */
1352 while (NextEntry != ListHead)
1353 {
1354 /* Get the loader entry */
1355 LdrEntry = CONTAINING_RECORD(NextEntry,
1356 LDR_DATA_TABLE_ENTRY,
1357 InLoadOrderLinks);
1358
1359 /* FIXME: ROS HACK. Make sure this is a driver */
1360 if (!RtlImageNtHeader(LdrEntry->DllBase))
1361 {
1362 /* Skip this entry */
1363 NextEntry= NextEntry->Flink;
1364 continue;
1365 }
1366
1367 /* Calculate the size we'll need and allocate a copy */
1368 EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
1369 LdrEntry->BaseDllName.MaximumLength +
1370 sizeof(UNICODE_NULL);
1371 NewEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_LDR_WSTR);
1372 if (!NewEntry) return FALSE;
1373
1374 /* Copy the entry over */
1375 *NewEntry = *LdrEntry;
1376
1377 /* Allocate the name */
1378 NewEntry->FullDllName.Buffer =
1379 ExAllocatePoolWithTag(PagedPool,
1380 LdrEntry->FullDllName.MaximumLength +
1381 sizeof(UNICODE_NULL),
1382 TAG_LDR_WSTR);
1383 if (!NewEntry->FullDllName.Buffer) return FALSE;
1384
1385 /* Set the base name */
1386 NewEntry->BaseDllName.Buffer = (PVOID)(NewEntry + 1);
1387
1388 /* Copy the full and base name */
1389 RtlCopyMemory(NewEntry->FullDllName.Buffer,
1390 LdrEntry->FullDllName.Buffer,
1391 LdrEntry->FullDllName.MaximumLength);
1392 RtlCopyMemory(NewEntry->BaseDllName.Buffer,
1393 LdrEntry->BaseDllName.Buffer,
1394 LdrEntry->BaseDllName.MaximumLength);
1395
1396 /* Null-terminate the base name */
1397 NewEntry->BaseDllName.Buffer[NewEntry->BaseDllName.Length /
1398 sizeof(WCHAR)] = UNICODE_NULL;
1399
1400 /* Insert the entry into the list */
1401 InsertTailList(&PsLoadedModuleList, &NewEntry->InLoadOrderLinks);
1402 NextEntry = NextEntry->Flink;
1403 }
1404
1405 /* Build the import lists for the boot drivers */
1406 //MiBuildImportsForBootDrivers();
1407
1408 /* We're done */
1409 return TRUE;
1410 }
1411
1412 BOOLEAN
1413 NTAPI
1414 MmVerifyImageIsOkForMpUse(IN PVOID BaseAddress)
1415 {
1416 PIMAGE_NT_HEADERS NtHeader;
1417 PAGED_CODE();
1418
1419 /* Get NT Headers */
1420 NtHeader = RtlImageNtHeader(BaseAddress);
1421 if (NtHeader)
1422 {
1423 /* Check if this image is only safe for UP while we have 2+ CPUs */
1424 if ((KeNumberProcessors > 1) &&
1425 (NtHeader->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY))
1426 {
1427 /* Fail */
1428 return FALSE;
1429 }
1430 }
1431
1432 /* Otherwise, it's safe */
1433 return TRUE;
1434 }
1435
1436 NTSTATUS
1437 NTAPI
1438 MmCheckSystemImage(IN HANDLE ImageHandle,
1439 IN BOOLEAN PurgeSection)
1440 {
1441 NTSTATUS Status;
1442 HANDLE SectionHandle;
1443 PVOID ViewBase = NULL;
1444 SIZE_T ViewSize = 0;
1445 IO_STATUS_BLOCK IoStatusBlock;
1446 FILE_STANDARD_INFORMATION FileStandardInfo;
1447 KAPC_STATE ApcState;
1448 PAGED_CODE();
1449
1450 /* Create a section for the DLL */
1451 Status = ZwCreateSection(&SectionHandle,
1452 SECTION_MAP_EXECUTE,
1453 NULL,
1454 NULL,
1455 PAGE_EXECUTE,
1456 SEC_COMMIT,
1457 ImageHandle);
1458 if (!NT_SUCCESS(Status)) return Status;
1459
1460 /* Make sure we're in the system process */
1461 KeStackAttachProcess(&PsInitialSystemProcess->Pcb, &ApcState);
1462
1463 /* Map it */
1464 Status = ZwMapViewOfSection(SectionHandle,
1465 NtCurrentProcess(),
1466 &ViewBase,
1467 0,
1468 0,
1469 NULL,
1470 &ViewSize,
1471 ViewShare,
1472 0,
1473 PAGE_EXECUTE);
1474 if (!NT_SUCCESS(Status))
1475 {
1476 /* We failed, close the handle and return */
1477 KeUnstackDetachProcess(&ApcState);
1478 ZwClose(SectionHandle);
1479 return Status;
1480 }
1481
1482 /* Now query image information */
1483 Status = ZwQueryInformationFile(ImageHandle,
1484 &IoStatusBlock,
1485 &FileStandardInfo,
1486 sizeof(FileStandardInfo),
1487 FileStandardInformation);
1488 if ( NT_SUCCESS(Status) )
1489 {
1490 /* First, verify the checksum */
1491 if (!LdrVerifyMappedImageMatchesChecksum(ViewBase,
1492 FileStandardInfo.
1493 EndOfFile.LowPart,
1494 FileStandardInfo.
1495 EndOfFile.LowPart))
1496 {
1497 /* Set checksum failure */
1498 Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
1499 }
1500
1501 /* Check that it's a valid SMP image if we have more then one CPU */
1502 if (!MmVerifyImageIsOkForMpUse(ViewBase))
1503 {
1504 /* Otherwise it's not the right image */
1505 Status = STATUS_IMAGE_MP_UP_MISMATCH;
1506 }
1507 }
1508
1509 /* Unmap the section, close the handle, and return status */
1510 ZwUnmapViewOfSection(NtCurrentProcess(), ViewBase);
1511 KeUnstackDetachProcess(&ApcState);
1512 ZwClose(SectionHandle);
1513 return Status;
1514 }
1515
1516 NTSTATUS
1517 NTAPI
1518 MmLoadSystemImage(IN PUNICODE_STRING FileName,
1519 IN PUNICODE_STRING NamePrefix OPTIONAL,
1520 IN PUNICODE_STRING LoadedName OPTIONAL,
1521 IN ULONG Flags,
1522 OUT PVOID *ModuleObject,
1523 OUT PVOID *ImageBaseAddress)
1524 {
1525 PVOID ModuleLoadBase = NULL;
1526 NTSTATUS Status;
1527 HANDLE FileHandle = NULL;
1528 OBJECT_ATTRIBUTES ObjectAttributes;
1529 IO_STATUS_BLOCK IoStatusBlock;
1530 PIMAGE_NT_HEADERS NtHeader;
1531 UNICODE_STRING BaseName, BaseDirectory, PrefixName, UnicodeTemp;
1532 PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
1533 ULONG EntrySize, DriverSize;
1534 PLOAD_IMPORTS LoadedImports = (PVOID)-2;
1535 PCHAR MissingApiName, Buffer;
1536 PWCHAR MissingDriverName;
1537 HANDLE SectionHandle;
1538 ACCESS_MASK DesiredAccess;
1539 PVOID Section = NULL;
1540 BOOLEAN LockOwned = FALSE;
1541 PLIST_ENTRY NextEntry;
1542 IMAGE_INFO ImageInfo;
1543 STRING AnsiTemp;
1544 PAGED_CODE();
1545
1546 /* Detect session-load */
1547 if (Flags)
1548 {
1549 /* Sanity checks */
1550 ASSERT(NamePrefix == NULL);
1551 ASSERT(LoadedName == NULL);
1552
1553 /* Make sure the process is in session too */
1554 if (!PsGetCurrentProcess()->ProcessInSession) return STATUS_NO_MEMORY;
1555 }
1556
1557 /* Allocate a buffer we'll use for names */
1558 Buffer = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH, TAG_LDR_WSTR);
1559 if (!Buffer) return STATUS_INSUFFICIENT_RESOURCES;
1560
1561 /* Check for a separator */
1562 if (FileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR)
1563 {
1564 PWCHAR p;
1565 ULONG BaseLength;
1566
1567 /* Loop the path until we get to the base name */
1568 p = &FileName->Buffer[FileName->Length / sizeof(WCHAR)];
1569 while (*(p - 1) != OBJ_NAME_PATH_SEPARATOR) p--;
1570
1571 /* Get the length */
1572 BaseLength = (ULONG)(&FileName->Buffer[FileName->Length / sizeof(WCHAR)] - p);
1573 BaseLength *= sizeof(WCHAR);
1574
1575 /* Setup the string */
1576 BaseName.Length = (USHORT)BaseLength;
1577 BaseName.Buffer = p;
1578 }
1579 else
1580 {
1581 /* Otherwise, we already have a base name */
1582 BaseName.Length = FileName->Length;
1583 BaseName.Buffer = FileName->Buffer;
1584 }
1585
1586 /* Setup the maximum length */
1587 BaseName.MaximumLength = BaseName.Length;
1588
1589 /* Now compute the base directory */
1590 BaseDirectory = *FileName;
1591 BaseDirectory.Length -= BaseName.Length;
1592 BaseDirectory.MaximumLength = BaseDirectory.Length;
1593
1594 /* And the prefix, which for now is just the name itself */
1595 PrefixName = *FileName;
1596
1597 /* Check if we have a prefix */
1598 if (NamePrefix) DPRINT1("Prefixed images are not yet supported!\n");
1599
1600 /* Check if we already have a name, use it instead */
1601 if (LoadedName) BaseName = *LoadedName;
1602
1603 /* Acquire the load lock */
1604 LoaderScan:
1605 ASSERT(LockOwned == FALSE);
1606 LockOwned = TRUE;
1607 KeEnterCriticalRegion();
1608 KeWaitForSingleObject(&MmSystemLoadLock,
1609 WrVirtualMemory,
1610 KernelMode,
1611 FALSE,
1612 NULL);
1613
1614 /* Scan the module list */
1615 NextEntry = PsLoadedModuleList.Flink;
1616 while (NextEntry != &PsLoadedModuleList)
1617 {
1618 /* Get the entry and compare the names */
1619 LdrEntry = CONTAINING_RECORD(NextEntry,
1620 LDR_DATA_TABLE_ENTRY,
1621 InLoadOrderLinks);
1622 if (RtlEqualUnicodeString(&PrefixName, &LdrEntry->FullDllName, TRUE))
1623 {
1624 /* Found it, break out */
1625 break;
1626 }
1627
1628 /* Keep scanning */
1629 NextEntry = NextEntry->Flink;
1630 }
1631
1632 /* Check if we found the image */
1633 if (NextEntry != &PsLoadedModuleList)
1634 {
1635 /* Check if we had already mapped a section */
1636 if (Section)
1637 {
1638 /* Dereference and clear */
1639 ObDereferenceObject(Section);
1640 Section = NULL;
1641 }
1642
1643 /* Check if this was supposed to be a session load */
1644 if (!Flags)
1645 {
1646 /* It wasn't, so just return the data */
1647 *ModuleObject = LdrEntry;
1648 *ImageBaseAddress = LdrEntry->DllBase;
1649 Status = STATUS_IMAGE_ALREADY_LOADED;
1650 }
1651 else
1652 {
1653 /* We don't support session loading yet */
1654 DPRINT1("Unsupported Session-Load!\n");
1655 while (TRUE);
1656 }
1657
1658 /* Do cleanup */
1659 goto Quickie;
1660 }
1661 else if (!Section)
1662 {
1663 /* It wasn't loaded, and we didn't have a previous attempt */
1664 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
1665 KeLeaveCriticalRegion();
1666 LockOwned = FALSE;
1667
1668 /* Check if KD is enabled */
1669 if ((KdDebuggerEnabled) && !(KdDebuggerNotPresent))
1670 {
1671 /* FIXME: Attempt to get image from KD */
1672 }
1673
1674 /* We don't have a valid entry */
1675 LdrEntry = NULL;
1676
1677 /* Setup image attributes */
1678 InitializeObjectAttributes(&ObjectAttributes,
1679 FileName,
1680 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1681 NULL,
1682 NULL);
1683
1684 /* Open the image */
1685 Status = ZwOpenFile(&FileHandle,
1686 FILE_EXECUTE,
1687 &ObjectAttributes,
1688 &IoStatusBlock,
1689 FILE_SHARE_READ | FILE_SHARE_DELETE,
1690 0);
1691 if (!NT_SUCCESS(Status)) goto Quickie;
1692
1693 /* Validate it */
1694 Status = MmCheckSystemImage(FileHandle, FALSE);
1695 if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
1696 (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
1697 (Status == STATUS_INVALID_IMAGE_FORMAT))
1698 {
1699 /* Fail loading */
1700 goto Quickie;
1701 }
1702
1703 /* Check if this is a session-load */
1704 if (Flags)
1705 {
1706 /* Then we only need read and execute */
1707 DesiredAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
1708 }
1709 else
1710 {
1711 /* Otherwise, we can allow write access */
1712 DesiredAccess = SECTION_ALL_ACCESS;
1713 }
1714
1715 /* Initialize the attributes for the section */
1716 InitializeObjectAttributes(&ObjectAttributes,
1717 NULL,
1718 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
1719 NULL,
1720 NULL);
1721
1722 /* Create the section */
1723 Status = ZwCreateSection(&SectionHandle,
1724 DesiredAccess,
1725 &ObjectAttributes,
1726 NULL,
1727 PAGE_EXECUTE,
1728 SEC_IMAGE,
1729 FileHandle);
1730 if (!NT_SUCCESS(Status)) goto Quickie;
1731
1732 /* Now get the section pointer */
1733 Status = ObReferenceObjectByHandle(SectionHandle,
1734 SECTION_MAP_EXECUTE,
1735 MmSectionObjectType,
1736 KernelMode,
1737 &Section,
1738 NULL);
1739 ZwClose(SectionHandle);
1740 if (!NT_SUCCESS(Status)) goto Quickie;
1741
1742 /* Check if this was supposed to be a session-load */
1743 if (Flags)
1744 {
1745 /* We don't support session loading yet */
1746 DPRINT1("Unsupported Session-Load!\n");
1747 while (TRUE);
1748 }
1749
1750 /* Check the loader list again, we should end up in the path below */
1751 goto LoaderScan;
1752 }
1753 else
1754 {
1755 /* We don't have a valid entry */
1756 LdrEntry = NULL;
1757 }
1758
1759 /* Load the image */
1760 Status = MiLoadImageSection(&Section,
1761 &ModuleLoadBase,
1762 FileName,
1763 FALSE,
1764 NULL);
1765 ASSERT(Status != STATUS_ALREADY_COMMITTED);
1766
1767 /* Get the size of the driver */
1768 DriverSize = ((PROS_SECTION_OBJECT)Section)->ImageSection->ImageSize;
1769
1770 /* Make sure we're not being loaded into session space */
1771 if (!Flags)
1772 {
1773 /* Check for success */
1774 if (NT_SUCCESS(Status))
1775 {
1776 /* FIXME: Support large pages for drivers */
1777 }
1778
1779 /* Dereference the section */
1780 ObDereferenceObject(Section);
1781 Section = NULL;
1782 }
1783
1784 /* Get the NT Header */
1785 NtHeader = RtlImageNtHeader(ModuleLoadBase);
1786
1787 /* Relocate the driver */
1788 Status = LdrRelocateImageWithBias(ModuleLoadBase,
1789 0,
1790 "SYSLDR",
1791 STATUS_SUCCESS,
1792 STATUS_CONFLICTING_ADDRESSES,
1793 STATUS_INVALID_IMAGE_FORMAT);
1794 if (!NT_SUCCESS(Status)) goto Quickie;
1795
1796 /* Calculate the size we'll need for the entry and allocate it */
1797 EntrySize = sizeof(LDR_DATA_TABLE_ENTRY) +
1798 BaseName.Length +
1799 sizeof(UNICODE_NULL);
1800
1801 /* Allocate the entry */
1802 LdrEntry = ExAllocatePoolWithTag(NonPagedPool, EntrySize, TAG_MODULE_OBJECT);
1803 if (!LdrEntry)
1804 {
1805 /* Fail */
1806 Status = STATUS_INSUFFICIENT_RESOURCES;
1807 goto Quickie;
1808 }
1809
1810 /* Setup the entry */
1811 LdrEntry->Flags = LDRP_LOAD_IN_PROGRESS;
1812 LdrEntry->LoadCount = 1;
1813 LdrEntry->LoadedImports = LoadedImports;
1814 LdrEntry->PatchInformation = NULL;
1815
1816 /* Check the version */
1817 if ((NtHeader->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
1818 (NtHeader->OptionalHeader.MajorImageVersion >= 5))
1819 {
1820 /* Mark this image as a native image */
1821 LdrEntry->Flags |= LDRP_ENTRY_NATIVE;
1822 }
1823
1824 /* Setup the rest of the entry */
1825 LdrEntry->DllBase = ModuleLoadBase;
1826 LdrEntry->EntryPoint = (PVOID)((ULONG_PTR)ModuleLoadBase +
1827 NtHeader->OptionalHeader.AddressOfEntryPoint);
1828 LdrEntry->SizeOfImage = DriverSize;
1829 LdrEntry->CheckSum = NtHeader->OptionalHeader.CheckSum;
1830 LdrEntry->SectionPointer = Section;
1831
1832 /* Now write the DLL name */
1833 LdrEntry->BaseDllName.Buffer = (PVOID)(LdrEntry + 1);
1834 LdrEntry->BaseDllName.Length = BaseName.Length;
1835 LdrEntry->BaseDllName.MaximumLength = BaseName.Length;
1836
1837 /* Copy and null-terminate it */
1838 RtlCopyMemory(LdrEntry->BaseDllName.Buffer,
1839 BaseName.Buffer,
1840 BaseName.Length);
1841 LdrEntry->BaseDllName.Buffer[BaseName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1842
1843 /* Now allocate the full name */
1844 LdrEntry->FullDllName.Buffer = ExAllocatePoolWithTag(PagedPool,
1845 PrefixName.Length +
1846 sizeof(UNICODE_NULL),
1847 TAG_LDR_WSTR);
1848 if (!LdrEntry->FullDllName.Buffer)
1849 {
1850 /* Don't fail, just set it to zero */
1851 LdrEntry->FullDllName.Length = 0;
1852 LdrEntry->FullDllName.MaximumLength = 0;
1853 }
1854 else
1855 {
1856 /* Set it up */
1857 LdrEntry->FullDllName.Length = PrefixName.Length;
1858 LdrEntry->FullDllName.MaximumLength = PrefixName.Length;
1859
1860 /* Copy and null-terminate */
1861 RtlCopyMemory(LdrEntry->FullDllName.Buffer,
1862 PrefixName.Buffer,
1863 PrefixName.Length);
1864 LdrEntry->FullDllName.Buffer[PrefixName.Length / sizeof(WCHAR)] = UNICODE_NULL;
1865 }
1866
1867 /* Add the entry */
1868 MiProcessLoaderEntry(LdrEntry, TRUE);
1869
1870 /* Resolve imports */
1871 MissingApiName = Buffer;
1872 Status = MiResolveImageReferences(ModuleLoadBase,
1873 &BaseDirectory,
1874 NULL,
1875 &MissingApiName,
1876 &MissingDriverName,
1877 &LoadedImports);
1878 if (!NT_SUCCESS(Status))
1879 {
1880 /* Fail */
1881 MiProcessLoaderEntry(LdrEntry, FALSE);
1882
1883 /* Check if we need to free the name */
1884 if (LdrEntry->FullDllName.Buffer)
1885 {
1886 /* Free it */
1887 ExFreePool(LdrEntry->FullDllName.Buffer);
1888 }
1889
1890 /* Free the entry itself */
1891 ExFreePoolWithTag(LdrEntry, TAG_MODULE_OBJECT);
1892 LdrEntry = NULL;
1893 goto Quickie;
1894 }
1895
1896 /* Update the loader entry */
1897 LdrEntry->Flags |= (LDRP_SYSTEM_MAPPED |
1898 LDRP_ENTRY_PROCESSED |
1899 LDRP_MM_LOADED);
1900 LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
1901 LdrEntry->LoadedImports = LoadedImports;
1902
1903 /* FIXME: Apply driver verifier */
1904
1905 /* FIXME: Write-protect the system image */
1906
1907 /* Check if notifications are enabled */
1908 if (PsImageNotifyEnabled)
1909 {
1910 /* Fill out the notification data */
1911 ImageInfo.Properties = 0;
1912 ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
1913 ImageInfo.SystemModeImage = TRUE;
1914 ImageInfo.ImageSize = LdrEntry->SizeOfImage;
1915 ImageInfo.ImageBase = LdrEntry->DllBase;
1916 ImageInfo.ImageSectionNumber = ImageInfo.ImageSelector = 0;
1917
1918 /* Send the notification */
1919 PspRunLoadImageNotifyRoutines(FileName, NULL, &ImageInfo);
1920 }
1921
1922 #if defined(KDBG) || defined(_WINKD_)
1923 /* MiCacheImageSymbols doesn't detect rossym */
1924 if (TRUE)
1925 #else
1926 /* Check if there's symbols */
1927 if (MiCacheImageSymbols(LdrEntry->DllBase))
1928 #endif
1929 {
1930 /* Check if the system root is present */
1931 if ((PrefixName.Length > (11 * sizeof(WCHAR))) &&
1932 !(_wcsnicmp(PrefixName.Buffer, L"\\SystemRoot", 11)))
1933 {
1934 /* Add the system root */
1935 UnicodeTemp = PrefixName;
1936 UnicodeTemp.Buffer += 11;
1937 UnicodeTemp.Length -= (11 * sizeof(WCHAR));
1938 sprintf_nt(Buffer,
1939 "%ws%wZ",
1940 &SharedUserData->NtSystemRoot[2],
1941 &UnicodeTemp);
1942 }
1943 else
1944 {
1945 /* Build the name */
1946 sprintf_nt(Buffer, "%wZ", &BaseName);
1947 }
1948
1949 /* Setup the ansi string */
1950 RtlInitString(&AnsiTemp, Buffer);
1951
1952 /* Notify the debugger */
1953 DbgLoadImageSymbols(&AnsiTemp,
1954 LdrEntry->DllBase,
1955 (ULONG_PTR)ZwCurrentProcess());
1956 LdrEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
1957 }
1958
1959 /* FIXME: Page the driver */
1960 ASSERT(Section == NULL);
1961
1962 /* Return pointers */
1963 *ModuleObject = LdrEntry;
1964 *ImageBaseAddress = LdrEntry->DllBase;
1965
1966 Quickie:
1967 /* If we have a file handle, close it */
1968 if (FileHandle) ZwClose(FileHandle);
1969
1970 /* Check if we have the lock acquired */
1971 if (LockOwned)
1972 {
1973 /* Release the lock */
1974 KeReleaseMutant(&MmSystemLoadLock, 1, FALSE, FALSE);
1975 KeLeaveCriticalRegion();
1976 LockOwned = FALSE;
1977 }
1978
1979 /* Check if we had a prefix */
1980 if (NamePrefix) ExFreePool(PrefixName.Buffer);
1981
1982 /* Free the name buffer and return status */
1983 ExFreePoolWithTag(Buffer, TAG_LDR_WSTR);
1984 return Status;
1985 }
1986
1987 /*
1988 * @implemented
1989 */
1990 PVOID
1991 NTAPI
1992 MmGetSystemRoutineAddress(IN PUNICODE_STRING SystemRoutineName)
1993 {
1994 PVOID ProcAddress = NULL;
1995 ANSI_STRING AnsiRoutineName;
1996 NTSTATUS Status;
1997 PLIST_ENTRY NextEntry;
1998 PLDR_DATA_TABLE_ENTRY LdrEntry;
1999 BOOLEAN Found = FALSE;
2000 UNICODE_STRING KernelName = RTL_CONSTANT_STRING(L"ntoskrnl.exe");
2001 UNICODE_STRING HalName = RTL_CONSTANT_STRING(L"hal.dll");
2002 ULONG Modules = 0;
2003
2004 /* Convert routine to ansi name */
2005 Status = RtlUnicodeStringToAnsiString(&AnsiRoutineName,
2006 SystemRoutineName,
2007 TRUE);
2008 if (!NT_SUCCESS(Status)) return NULL;
2009
2010 /* Lock the list */
2011 KeEnterCriticalRegion();
2012 ExAcquireResourceSharedLite(&PsLoadedModuleResource, TRUE);
2013
2014 /* Loop the loaded module list */
2015 NextEntry = PsLoadedModuleList.Flink;
2016 while (NextEntry != &PsLoadedModuleList)
2017 {
2018 /* Get the entry */
2019 LdrEntry = CONTAINING_RECORD(NextEntry,
2020 LDR_DATA_TABLE_ENTRY,
2021 InLoadOrderLinks);
2022
2023 /* Check if it's the kernel or HAL */
2024 if (RtlEqualUnicodeString(&KernelName, &LdrEntry->BaseDllName, TRUE))
2025 {
2026 /* Found it */
2027 Found = TRUE;
2028 Modules++;
2029 }
2030 else if (RtlEqualUnicodeString(&HalName, &LdrEntry->BaseDllName, TRUE))
2031 {
2032 /* Found it */
2033 Found = TRUE;
2034 Modules++;
2035 }
2036
2037 /* Check if we found a valid binary */
2038 if (Found)
2039 {
2040 /* Find the procedure name */
2041 ProcAddress = MiFindExportedRoutineByName(LdrEntry->DllBase,
2042 &AnsiRoutineName);
2043
2044 /* Break out if we found it or if we already tried both modules */
2045 if (ProcAddress) break;
2046 if (Modules == 2) break;
2047 }
2048
2049 /* Keep looping */
2050 NextEntry = NextEntry->Flink;
2051 }
2052
2053 /* Release the lock */
2054 ExReleaseResourceLite(&PsLoadedModuleResource);
2055 KeLeaveCriticalRegion();
2056
2057 /* Free the string and return */
2058 RtlFreeAnsiString(&AnsiRoutineName);
2059 return ProcAddress;
2060 }
2061