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