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