Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

7767 lines
229 KiB

  1. /*++
  2. Copyright (c) 1991 Microsoft Corporation
  3. Module Name:
  4. sysload.c
  5. Abstract:
  6. This module contains the code to load DLLs into the system portion of
  7. the address space and calls the DLL at its initialization entry point.
  8. Author:
  9. Lou Perazzoli 21-May-1991
  10. Landy Wang 02-June-1997
  11. Revision History:
  12. --*/
  13. #include "mi.h"
  14. KMUTANT MmSystemLoadLock;
  15. ULONG MmTotalSystemDriverPages;
  16. ULONG MmDriverCommit;
  17. ULONG MiFirstDriverLoadEver = 0;
  18. //
  19. // This key is set to TRUE to make more memory below 16mb available for drivers.
  20. // It can be cleared via the registry.
  21. //
  22. LOGICAL MmMakeLowMemory = TRUE;
  23. //
  24. // Enabled via the registry to identify drivers which unload without releasing
  25. // resources or still have active timers, etc.
  26. //
  27. PUNLOADED_DRIVERS MmUnloadedDrivers;
  28. ULONG MmLastUnloadedDriver;
  29. ULONG MiTotalUnloads;
  30. ULONG MiUnloadsSkipped;
  31. //
  32. // This can be set by the registry.
  33. //
  34. ULONG MmEnforceWriteProtection = 1;
  35. //
  36. // Referenced by ke\bugcheck.c.
  37. //
  38. PVOID ExPoolCodeStart;
  39. PVOID ExPoolCodeEnd;
  40. PVOID MmPoolCodeStart;
  41. PVOID MmPoolCodeEnd;
  42. PVOID MmPteCodeStart;
  43. PVOID MmPteCodeEnd;
  44. extern LONG MiSessionLeaderExists;
  45. ULONG
  46. CacheImageSymbols (
  47. IN PVOID ImageBase
  48. );
  49. NTSTATUS
  50. MiResolveImageReferences (
  51. PVOID ImageBase,
  52. IN PUNICODE_STRING ImageFileDirectory,
  53. IN PUNICODE_STRING NamePrefix OPTIONAL,
  54. OUT PCHAR *MissingProcedureName,
  55. OUT PWSTR *MissingDriverName,
  56. OUT PLOAD_IMPORTS *LoadedImports
  57. );
  58. NTSTATUS
  59. MiSnapThunk (
  60. IN PVOID DllBase,
  61. IN PVOID ImageBase,
  62. IN PIMAGE_THUNK_DATA NameThunk,
  63. OUT PIMAGE_THUNK_DATA AddrThunk,
  64. IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
  65. IN ULONG ExportSize,
  66. IN LOGICAL SnapForwarder,
  67. OUT PCHAR *MissingProcedureName
  68. );
  69. NTSTATUS
  70. MiLoadImageSection (
  71. IN PSECTION SectionPointer,
  72. OUT PVOID *ImageBase,
  73. IN PUNICODE_STRING ImageFileName,
  74. IN ULONG LoadInSessionSpace
  75. );
  76. VOID
  77. MiEnablePagingOfDriver (
  78. IN PVOID ImageHandle
  79. );
  80. VOID
  81. MiSetPagingOfDriver (
  82. IN PMMPTE PointerPte,
  83. IN PMMPTE LastPte,
  84. IN LOGICAL SessionSpace
  85. );
  86. PVOID
  87. MiLookupImageSectionByName (
  88. IN PVOID Base,
  89. IN LOGICAL MappedAsImage,
  90. IN PCHAR SectionName,
  91. OUT PULONG SectionSize
  92. );
  93. VOID
  94. MiClearImports (
  95. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  96. );
  97. NTSTATUS
  98. MiBuildImportsForBootDrivers (
  99. VOID
  100. );
  101. NTSTATUS
  102. MmCheckSystemImage (
  103. IN HANDLE ImageFileHandle,
  104. IN LOGICAL PurgeSection
  105. );
  106. LONG
  107. MiMapCacheExceptionFilter (
  108. OUT PNTSTATUS Status,
  109. IN PEXCEPTION_POINTERS ExceptionPointer
  110. );
  111. ULONG
  112. MiSetProtectionOnTransitionPte (
  113. IN PMMPTE PointerPte,
  114. IN ULONG ProtectionMask
  115. );
  116. NTSTATUS
  117. MiDereferenceImports (
  118. IN PLOAD_IMPORTS ImportList
  119. );
  120. LOGICAL
  121. MiCallDllUnloadAndUnloadDll (
  122. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  123. );
  124. PVOID
  125. MiLocateExportName (
  126. IN PVOID DllBase,
  127. IN PCHAR FunctionName
  128. );
  129. VOID
  130. MiRememberUnloadedDriver (
  131. IN PUNICODE_STRING DriverName,
  132. IN PVOID Address,
  133. IN ULONG Length
  134. );
  135. VOID
  136. MiWriteProtectSystemImage (
  137. IN PVOID DllBase
  138. );
  139. VOID
  140. MiLocateKernelSections (
  141. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  142. );
  143. VOID
  144. MiUpdateThunks (
  145. IN PLOADER_PARAMETER_BLOCK LoaderBlock,
  146. IN PVOID OldAddress,
  147. IN PVOID NewAddress,
  148. IN ULONG NumberOfBytes
  149. );
  150. PVOID
  151. MiFindExportedRoutineByName (
  152. IN PVOID DllBase,
  153. IN PANSI_STRING AnsiImageRoutineName
  154. );
  155. #if 0
  156. VOID
  157. MiLockDriverPdata (
  158. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  159. );
  160. #endif
  161. VOID
  162. MiSetSystemCodeProtection (
  163. IN PMMPTE FirstPte,
  164. IN PMMPTE LastPte
  165. );
  166. LOGICAL
  167. MiChargeResidentAvailable (
  168. IN PFN_NUMBER NumberOfPages,
  169. IN ULONG Id
  170. );
  171. VOID
  172. MiReturnResidentAvailable (
  173. IN PFN_NUMBER NumberOfPages,
  174. IN ULONG Id
  175. );
  176. #ifdef ALLOC_PRAGMA
  177. #pragma alloc_text(PAGE,MmCheckSystemImage)
  178. #pragma alloc_text(PAGE,MmLoadSystemImage)
  179. #pragma alloc_text(PAGE,MiResolveImageReferences)
  180. #pragma alloc_text(PAGE,MiSnapThunk)
  181. #pragma alloc_text(PAGE,MiEnablePagingOfDriver)
  182. #pragma alloc_text(PAGE,MmPageEntireDriver)
  183. #pragma alloc_text(PAGE,MiSetImageProtect)
  184. #pragma alloc_text(PAGE,MiDereferenceImports)
  185. #pragma alloc_text(PAGE,MiCallDllUnloadAndUnloadDll)
  186. #pragma alloc_text(PAGE,MiLocateExportName)
  187. #pragma alloc_text(PAGE,MiClearImports)
  188. #pragma alloc_text(PAGE,MiWriteProtectSystemImage)
  189. #pragma alloc_text(PAGE,MmGetSystemRoutineAddress)
  190. #pragma alloc_text(PAGE,MiFindExportedRoutineByName)
  191. #pragma alloc_text(PAGE,MmCallDllInitialize)
  192. #pragma alloc_text(PAGE,MmFreeDriverInitialization)
  193. #pragma alloc_text(PAGE,MmResetDriverPaging)
  194. #pragma alloc_text(PAGE,MmUnloadSystemImage)
  195. #pragma alloc_text(PAGE,MiLoadImageSection)
  196. #pragma alloc_text(PAGE,MiRememberUnloadedDriver)
  197. #pragma alloc_text(INIT,MiBuildImportsForBootDrivers)
  198. #pragma alloc_text(INIT,MiReloadBootLoadedDrivers)
  199. #pragma alloc_text(INIT,MiUpdateThunks)
  200. #pragma alloc_text(INIT,MiInitializeLoadedModuleList)
  201. #pragma alloc_text(INIT,MiLocateKernelSections)
  202. #if 0
  203. #pragma alloc_text(PAGEKD,MiLockDriverPdata)
  204. #endif
  205. #if !defined(NT_UP)
  206. #pragma alloc_text(PAGE,MmVerifyImageIsOkForMpUse)
  207. #endif
  208. #endif
  209. CHAR MiPteStr[] = "\0";
  210. VOID
  211. MiProcessLoaderEntry (
  212. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry,
  213. IN LOGICAL Insert
  214. )
  215. /*++
  216. Routine Description:
  217. This function is a nonpaged wrapper which acquires the PsLoadedModuleList
  218. lock to insert a new entry.
  219. Arguments:
  220. DataTableEntry - Supplies the loaded module list entry to insert/remove.
  221. Insert - Supplies TRUE if the entry should be inserted, FALSE if the entry
  222. should be removed.
  223. Return Value:
  224. None.
  225. Environment:
  226. Kernel mode. Normal APCs disabled (critical region held).
  227. --*/
  228. {
  229. KIRQL OldIrql;
  230. ExAcquireResourceExclusiveLite (&PsLoadedModuleResource, TRUE);
  231. ExAcquireSpinLock (&PsLoadedModuleSpinLock, &OldIrql);
  232. if (Insert == TRUE) {
  233. InsertTailList (&PsLoadedModuleList, &DataTableEntry->InLoadOrderLinks);
  234. #if defined(_AMD64_) // || defined(_IA64_)
  235. RtlInsertInvertedFunctionTable (&PsInvertedFunctionTable,
  236. DataTableEntry->DllBase,
  237. DataTableEntry->SizeOfImage);
  238. #endif
  239. }
  240. else {
  241. #if defined(_AMD64_) // || defined(_IA64_)
  242. RtlRemoveInvertedFunctionTable (&PsInvertedFunctionTable,
  243. DataTableEntry->DllBase);
  244. #endif
  245. RemoveEntryList (&DataTableEntry->InLoadOrderLinks);
  246. }
  247. ExReleaseSpinLock (&PsLoadedModuleSpinLock, OldIrql);
  248. ExReleaseResourceLite (&PsLoadedModuleResource);
  249. }
  250. NTSTATUS
  251. MmLoadSystemImage (
  252. IN PUNICODE_STRING ImageFileName,
  253. IN PUNICODE_STRING NamePrefix OPTIONAL,
  254. IN PUNICODE_STRING LoadedBaseName OPTIONAL,
  255. IN ULONG LoadFlags,
  256. OUT PVOID *ImageHandle,
  257. OUT PVOID *ImageBaseAddress
  258. )
  259. /*++
  260. Routine Description:
  261. This routine reads the image pages from the specified section into
  262. the system and returns the address of the DLL's header.
  263. At successful completion, the Section is referenced so it remains
  264. until the system image is unloaded.
  265. Arguments:
  266. ImageFileName - Supplies the full path name (including the image name)
  267. of the image to load.
  268. NamePrefix - If present, supplies the prefix to use with the image name on
  269. load operations. This is used to load the same image multiple
  270. times, by using different prefixes.
  271. LoadedBaseName - If present, supplies the base name to use on the
  272. loaded image instead of the base name found on the
  273. image name.
  274. LoadFlags - Supplies a combination of bit flags as follows:
  275. MM_LOAD_IMAGE_IN_SESSION :
  276. - Supplies whether to load this image in session space.
  277. Each session gets a different copy of this driver with
  278. pages shared as much as possible via copy on write.
  279. MM_LOAD_IMAGE_AND_LOCKDOWN :
  280. - Supplies TRUE if the image pages should be made
  281. nonpagable.
  282. ImageHandle - Returns an opaque pointer to the referenced section object
  283. of the image that was loaded.
  284. ImageBaseAddress - Returns the image base within the system.
  285. Return Value:
  286. Status of the load operation.
  287. Environment:
  288. Kernel mode, APC_LEVEL or below, arbitrary process context.
  289. --*/
  290. {
  291. SIZE_T DataTableEntrySize;
  292. PWSTR BaseDllNameBuffer;
  293. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  294. KLDR_DATA_TABLE_ENTRY TempDataTableEntry;
  295. NTSTATUS Status;
  296. PSECTION SectionPointer;
  297. PIMAGE_NT_HEADERS NtHeaders;
  298. UNICODE_STRING PrefixedImageName;
  299. UNICODE_STRING BaseName;
  300. UNICODE_STRING BaseDirectory;
  301. OBJECT_ATTRIBUTES ObjectAttributes;
  302. HANDLE FileHandle;
  303. HANDLE SectionHandle;
  304. IO_STATUS_BLOCK IoStatus;
  305. PCHAR NameBuffer;
  306. PLIST_ENTRY NextEntry;
  307. ULONG NumberOfPtes;
  308. PCHAR MissingProcedureName;
  309. PWSTR MissingDriverName;
  310. PWSTR PrintableMissingDriverName;
  311. PLOAD_IMPORTS LoadedImports;
  312. PMMSESSION Session;
  313. LOGICAL AlreadyOpen;
  314. LOGICAL IssueUnloadOnFailure;
  315. ULONG SectionAccess;
  316. PKTHREAD CurrentThread;
  317. PAGED_CODE();
  318. LoadedImports = (PLOAD_IMPORTS)NO_IMPORTS_USED;
  319. SectionPointer = (PVOID)-1;
  320. FileHandle = (HANDLE)0;
  321. MissingProcedureName = NULL;
  322. MissingDriverName = NULL;
  323. IssueUnloadOnFailure = FALSE;
  324. MiFirstDriverLoadEver |= 0x1;
  325. NameBuffer = ExAllocatePoolWithTag (NonPagedPool,
  326. MAXIMUM_FILENAME_LENGTH,
  327. 'nLmM');
  328. if (NameBuffer == NULL) {
  329. return STATUS_INSUFFICIENT_RESOURCES;
  330. }
  331. //
  332. // Initializing these is not needed for correctness, but
  333. // without it the compiler cannot compile this code W4 to check
  334. // for use of uninitialized variables.
  335. //
  336. NumberOfPtes = (ULONG)-1;
  337. DataTableEntry = NULL;
  338. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  339. ASSERT (NamePrefix == NULL);
  340. ASSERT (LoadedBaseName == NULL);
  341. if ((PsGetCurrentProcess()->Flags & PS_PROCESS_FLAGS_IN_SESSION) == 0) {
  342. ExFreePool (NameBuffer);
  343. return STATUS_NO_MEMORY;
  344. }
  345. ASSERT (MmIsAddressValid (MmSessionSpace) == TRUE);
  346. Session = &MmSessionSpace->Session;
  347. }
  348. else {
  349. Session = &MmSession;
  350. }
  351. //
  352. // Get name roots.
  353. //
  354. if (ImageFileName->Buffer[0] == OBJ_NAME_PATH_SEPARATOR) {
  355. PWCHAR p;
  356. ULONG l;
  357. p = &ImageFileName->Buffer[ImageFileName->Length>>1];
  358. while (*(p-1) != OBJ_NAME_PATH_SEPARATOR) {
  359. p--;
  360. }
  361. l = (ULONG)(&ImageFileName->Buffer[ImageFileName->Length>>1] - p);
  362. l *= sizeof(WCHAR);
  363. BaseName.Length = (USHORT)l;
  364. BaseName.Buffer = p;
  365. }
  366. else {
  367. BaseName.Length = ImageFileName->Length;
  368. BaseName.Buffer = ImageFileName->Buffer;
  369. }
  370. BaseName.MaximumLength = BaseName.Length;
  371. BaseDirectory = *ImageFileName;
  372. BaseDirectory.Length = (USHORT)(BaseDirectory.Length - BaseName.Length);
  373. BaseDirectory.MaximumLength = BaseDirectory.Length;
  374. PrefixedImageName = *ImageFileName;
  375. //
  376. // If there's a name prefix, add it to the PrefixedImageName.
  377. //
  378. if (NamePrefix) {
  379. PrefixedImageName.MaximumLength = (USHORT)(BaseDirectory.Length + NamePrefix->Length + BaseName.Length);
  380. PrefixedImageName.Buffer = ExAllocatePoolWithTag (
  381. NonPagedPool,
  382. PrefixedImageName.MaximumLength,
  383. 'dLmM');
  384. if (!PrefixedImageName.Buffer) {
  385. ExFreePool (NameBuffer);
  386. return STATUS_INSUFFICIENT_RESOURCES;
  387. }
  388. PrefixedImageName.Length = 0;
  389. RtlAppendUnicodeStringToString(&PrefixedImageName, &BaseDirectory);
  390. RtlAppendUnicodeStringToString(&PrefixedImageName, NamePrefix);
  391. RtlAppendUnicodeStringToString(&PrefixedImageName, &BaseName);
  392. //
  393. // Alter the basename to match.
  394. //
  395. BaseName.Buffer = PrefixedImageName.Buffer + BaseDirectory.Length / sizeof(WCHAR);
  396. BaseName.Length = (USHORT)(BaseName.Length + NamePrefix->Length);
  397. BaseName.MaximumLength = (USHORT)(BaseName.MaximumLength + NamePrefix->Length);
  398. }
  399. //
  400. // If there's a loaded base name, use it instead of the base name.
  401. //
  402. if (LoadedBaseName) {
  403. BaseName = *LoadedBaseName;
  404. }
  405. #if DBG
  406. if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
  407. DbgPrint ("MM:SYSLDR Loading %wZ (%wZ) %s\n",
  408. &PrefixedImageName,
  409. &BaseName,
  410. (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) ? "in session space" : " ");
  411. }
  412. #endif
  413. AlreadyOpen = FALSE;
  414. //
  415. // Arbitrary process context so prevent suspend APCs now.
  416. //
  417. CurrentThread = KeGetCurrentThread ();
  418. KeEnterCriticalRegionThread (CurrentThread);
  419. KeWaitForSingleObject (&MmSystemLoadLock,
  420. WrVirtualMemory,
  421. KernelMode,
  422. FALSE,
  423. (PLARGE_INTEGER)NULL);
  424. //
  425. // Check to see if this name already exists in the loader database.
  426. //
  427. NextEntry = PsLoadedModuleList.Flink;
  428. while (NextEntry != &PsLoadedModuleList) {
  429. DataTableEntry = CONTAINING_RECORD(NextEntry,
  430. KLDR_DATA_TABLE_ENTRY,
  431. InLoadOrderLinks);
  432. if (RtlEqualUnicodeString (&PrefixedImageName,
  433. &DataTableEntry->FullDllName,
  434. TRUE)) {
  435. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  436. if (MI_IS_SESSION_ADDRESS (DataTableEntry->DllBase) == FALSE) {
  437. //
  438. // The caller is trying to load a driver in session space
  439. // that has already been loaded in system space. This is
  440. // not allowed.
  441. //
  442. Status = STATUS_CONFLICTING_ADDRESSES;
  443. goto return2;
  444. }
  445. AlreadyOpen = TRUE;
  446. //
  447. // The LoadCount should generally not be 0 here, but it is
  448. // possible in the case where an attempt has been made to
  449. // unload a DLL on last dereference, but the DLL refused to
  450. // unload.
  451. //
  452. DataTableEntry->LoadCount += 1;
  453. SectionPointer = DataTableEntry->SectionPointer;
  454. break;
  455. }
  456. else {
  457. if (MI_IS_SESSION_ADDRESS (DataTableEntry->DllBase) == TRUE) {
  458. //
  459. // The caller is trying to load a driver in systemwide space
  460. // that has already been loaded in session space. This is
  461. // not allowed.
  462. //
  463. Status = STATUS_CONFLICTING_ADDRESSES;
  464. goto return2;
  465. }
  466. }
  467. *ImageHandle = DataTableEntry;
  468. *ImageBaseAddress = DataTableEntry->DllBase;
  469. Status = STATUS_IMAGE_ALREADY_LOADED;
  470. goto return2;
  471. }
  472. NextEntry = NextEntry->Flink;
  473. }
  474. ASSERT (AlreadyOpen == TRUE || NextEntry == &PsLoadedModuleList);
  475. if (AlreadyOpen == FALSE) {
  476. //
  477. // Check and see if a user wants to replace this binary
  478. // via a transfer through the kernel debugger. If this
  479. // fails just continue on with the existing file.
  480. //
  481. if (KdDebuggerEnabled && KdDebuggerNotPresent == FALSE) {
  482. Status = KdPullRemoteFile(ImageFileName,
  483. FILE_ATTRIBUTE_NORMAL,
  484. FILE_OVERWRITE_IF,
  485. FILE_SYNCHRONOUS_IO_NONALERT);
  486. if (NT_SUCCESS(Status)) {
  487. DbgPrint("MmLoadSystemImage: Pulled %wZ from kd\n",
  488. ImageFileName);
  489. }
  490. }
  491. DataTableEntry = NULL;
  492. //
  493. // Attempt to open the driver image itself. If this fails, then the
  494. // driver image cannot be located, so nothing else matters.
  495. //
  496. InitializeObjectAttributes (&ObjectAttributes,
  497. ImageFileName,
  498. (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
  499. NULL,
  500. NULL);
  501. Status = ZwOpenFile (&FileHandle,
  502. FILE_EXECUTE,
  503. &ObjectAttributes,
  504. &IoStatus,
  505. FILE_SHARE_READ | FILE_SHARE_DELETE,
  506. 0);
  507. if (!NT_SUCCESS(Status)) {
  508. #if DBG
  509. if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
  510. DbgPrint ("MiLoadImageSection: cannot open %wZ\n",
  511. ImageFileName);
  512. }
  513. #endif
  514. //
  515. // Don't raise hard error status for file not found.
  516. //
  517. goto return2;
  518. }
  519. Status = MmCheckSystemImage(FileHandle, FALSE);
  520. if ((Status == STATUS_IMAGE_CHECKSUM_MISMATCH) ||
  521. (Status == STATUS_IMAGE_MP_UP_MISMATCH) ||
  522. (Status == STATUS_INVALID_IMAGE_PROTECT)) {
  523. goto return1;
  524. }
  525. //
  526. // Now attempt to create an image section for the file. If this fails,
  527. // then the driver file is not an image. Session space drivers are
  528. // shared text with copy on write data, so don't allow writes here.
  529. //
  530. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  531. SectionAccess = SECTION_MAP_READ | SECTION_MAP_EXECUTE;
  532. }
  533. else {
  534. SectionAccess = SECTION_ALL_ACCESS;
  535. }
  536. InitializeObjectAttributes (&ObjectAttributes,
  537. NULL,
  538. (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
  539. NULL,
  540. NULL);
  541. Status = ZwCreateSection (&SectionHandle,
  542. SectionAccess,
  543. &ObjectAttributes,
  544. (PLARGE_INTEGER) NULL,
  545. PAGE_EXECUTE,
  546. SEC_IMAGE,
  547. FileHandle);
  548. if (!NT_SUCCESS(Status)) {
  549. goto return1;
  550. }
  551. //
  552. // Now reference the section handle. If this fails something is
  553. // very wrong because we are in a privileged process.
  554. //
  555. // N.B. ObRef sets SectionPointer to NULL on failure so it must be
  556. // reset to -1 in this case.
  557. //
  558. Status = ObReferenceObjectByHandle (SectionHandle,
  559. SECTION_MAP_EXECUTE,
  560. MmSectionObjectType,
  561. KernelMode,
  562. (PVOID *) &SectionPointer,
  563. (POBJECT_HANDLE_INFORMATION) NULL );
  564. ZwClose (SectionHandle);
  565. if (!NT_SUCCESS (Status)) {
  566. SectionPointer = (PVOID)-1; // undo ObRef setting.
  567. goto return1;
  568. }
  569. if (SectionPointer->Segment->ControlArea->NumberOfSubsections == 1) {
  570. if (((LoadFlags & MM_LOAD_IMAGE_IN_SESSION) == 0) &&
  571. (SectionPointer->Segment->BasedAddress != (PVOID)Session->SystemSpaceViewStart)) {
  572. PSECTION SectionPointer2;
  573. //
  574. // The driver was linked with subsection alignment such that
  575. // it is mapped with one subsection. Since the CreateSection
  576. // above guarantees that the driver image is indeed a
  577. // satisfactory executable, map it directly now to reuse the
  578. // cache from the MmCheckSystemImage call above.
  579. //
  580. Status = ZwCreateSection (&SectionHandle,
  581. SectionAccess,
  582. (POBJECT_ATTRIBUTES) NULL,
  583. (PLARGE_INTEGER) NULL,
  584. PAGE_EXECUTE,
  585. SEC_COMMIT,
  586. FileHandle );
  587. if (NT_SUCCESS(Status)) {
  588. Status = ObReferenceObjectByHandle (
  589. SectionHandle,
  590. SECTION_MAP_EXECUTE,
  591. MmSectionObjectType,
  592. KernelMode,
  593. (PVOID *) &SectionPointer2,
  594. (POBJECT_HANDLE_INFORMATION) NULL );
  595. ZwClose (SectionHandle);
  596. if (NT_SUCCESS (Status)) {
  597. //
  598. // The number of PTEs won't match if the image is
  599. // stripped and the debug directory crosses the last
  600. // sector boundary of the file. We could still use the
  601. // new section, but these cases are under 2% of all the
  602. // drivers loaded so don't bother.
  603. //
  604. if (SectionPointer->Segment->TotalNumberOfPtes == SectionPointer2->Segment->TotalNumberOfPtes) {
  605. ObDereferenceObject (SectionPointer);
  606. SectionPointer = SectionPointer2;
  607. }
  608. else {
  609. ObDereferenceObject (SectionPointer2);
  610. }
  611. }
  612. }
  613. }
  614. }
  615. }
  616. //
  617. // Load the driver from the filesystem and pick a virtual address for it.
  618. // For Hydra, this means also allocating session virtual space, and
  619. // after mapping a view of the image, either copying or sharing the
  620. // driver's code and data in the session virtual space.
  621. //
  622. // If it is a share map because the image was loaded at its based address,
  623. // the disk image will remain busy.
  624. //
  625. Status = MiLoadImageSection (SectionPointer,
  626. ImageBaseAddress,
  627. ImageFileName,
  628. LoadFlags & MM_LOAD_IMAGE_IN_SESSION);
  629. NumberOfPtes = SectionPointer->Segment->TotalNumberOfPtes;
  630. if (Status == STATUS_ALREADY_COMMITTED) {
  631. //
  632. // This is a driver that was relocated that is being loaded into
  633. // the same session space twice. Don't increment the overall load
  634. // count - just the image count in the session which has already been
  635. // done.
  636. //
  637. ASSERT (AlreadyOpen == TRUE);
  638. ASSERT (LoadFlags & MM_LOAD_IMAGE_IN_SESSION);
  639. ASSERT (DataTableEntry != NULL);
  640. ASSERT (DataTableEntry->LoadCount > 1);
  641. *ImageHandle = DataTableEntry;
  642. *ImageBaseAddress = DataTableEntry->DllBase;
  643. DataTableEntry->LoadCount -= 1;
  644. Status = STATUS_SUCCESS;
  645. goto return1;
  646. }
  647. if ((MiFirstDriverLoadEver & 0x2) == 0) {
  648. NTSTATUS PagingPathStatus;
  649. //
  650. // Check with all of the drivers along the path to win32k.sys to
  651. // ensure that they are willing to follow the rules required
  652. // of them and to give them a chance to lock down code and data
  653. // that needs to be locked. If any of the drivers along the path
  654. // refuses to participate, fail the win32k.sys load.
  655. //
  656. // It is assumed that all drivers live on the same physical drive, so
  657. // when the very first driver is loaded, this check can be made.
  658. // This eliminates the need to check for things like relocated win32ks,
  659. // Terminal Server systems, etc.
  660. //
  661. //
  662. // In WinPE removable media boot case don't do this since user might
  663. // be running WinPE in RAM and would like to swap out the boot media.
  664. //
  665. if (InitWinPEModeType & INIT_WINPEMODE_REMOVABLE_MEDIA) {
  666. PagingPathStatus = STATUS_SUCCESS;
  667. } else {
  668. PagingPathStatus = PpPagePathAssign(SectionPointer->Segment->ControlArea->FilePointer);
  669. }
  670. if (!NT_SUCCESS(PagingPathStatus)) {
  671. KdPrint (("PpPagePathAssign FAILED for win32k.sys: %x\n",
  672. PagingPathStatus));
  673. //
  674. // Failing the insertion of win32k.sys' device in the
  675. // pagefile path is commented out until the storage drivers have
  676. // been modified to correctly handle this request. If this is
  677. // added later, add code here to release relevant resources for
  678. // the error path.
  679. //
  680. }
  681. MiFirstDriverLoadEver |= 0x2;
  682. }
  683. //
  684. // Normal drivers are dereferenced here and their images can then be
  685. // overwritten. This is ok because we've already read the whole thing
  686. // into memory and from here until reboot (or unload), we back them
  687. // with the pagefile.
  688. //
  689. // win32k.sys and session space drivers are the exception - these images
  690. // are inpaged from the filesystem and we need to keep our reference to
  691. // the file so that it doesn't get overwritten.
  692. //
  693. if ((LoadFlags & MM_LOAD_IMAGE_IN_SESSION) == 0) {
  694. ObDereferenceObject (SectionPointer);
  695. SectionPointer = (PVOID)-1;
  696. }
  697. //
  698. // The module LoadCount will be 1 here if the module was just loaded.
  699. // The LoadCount will be >1 if it was attached to by a session (as opposed
  700. // to just loaded).
  701. //
  702. if (!NT_SUCCESS(Status)) {
  703. if (AlreadyOpen == TRUE) {
  704. //
  705. // We're failing and we were just attaching to an already loaded
  706. // driver. We don't want to go through the forced unload path
  707. // because we've already deleted the address space. Simply
  708. // decrement our reference and null the DataTableEntry
  709. // so we don't go through the forced unload path.
  710. //
  711. ASSERT (DataTableEntry != NULL);
  712. DataTableEntry->LoadCount -= 1;
  713. DataTableEntry = NULL;
  714. }
  715. goto return1;
  716. }
  717. //
  718. // Error recovery from this point out for sessions works as follows:
  719. //
  720. // For sessions, we may or may not have a DataTableEntry at this point.
  721. // If we do, it's because we're attaching to a driver that has already
  722. // been loaded - and the DataTableEntry->LoadCount has been bumped - so
  723. // the error recovery from here on out is to just call
  724. // MmUnloadSystemImage with the DataTableEntry.
  725. //
  726. // If this is the first load of a given driver into a session space, we
  727. // have no DataTableEntry at this point. The view has already been mapped
  728. // and committed and the group/session addresses reserved for this DLL.
  729. // The error recovery path handles all this because
  730. // MmUnloadSystemImage will zero the relevant fields in the
  731. // LDR_DATA_TABLE_ENTRY so that MmUnloadSystemImage will work properly.
  732. //
  733. IssueUnloadOnFailure = TRUE;
  734. if (((LoadFlags & MM_LOAD_IMAGE_IN_SESSION) == 0) ||
  735. (*ImageBaseAddress != SectionPointer->Segment->BasedAddress)) {
  736. #if DBG
  737. //
  738. // Warn users about session images that cannot be shared
  739. // because they were linked at a bad address.
  740. //
  741. if ((LoadFlags & MM_LOAD_IMAGE_IN_SESSION) &&
  742. (MmSessionSpace->SessionId != 0)) {
  743. DbgPrint ("MM: Session %d image %wZ is linked at a nonsharable address (%p)\n",
  744. MmSessionSpace->SessionId,
  745. ImageFileName,
  746. SectionPointer->Segment->BasedAddress);
  747. DbgPrint ("MM: Image %wZ has been moved to address (%p) by the system so it can run,\n",
  748. ImageFileName,
  749. *ImageBaseAddress);
  750. DbgPrint (" but this needs to be fixed in the image for sharing to occur.\n");
  751. }
  752. #endif
  753. //
  754. // Apply the fixups to the section.
  755. //
  756. try {
  757. Status = LdrRelocateImage (*ImageBaseAddress,
  758. "SYSLDR",
  759. STATUS_SUCCESS,
  760. STATUS_CONFLICTING_ADDRESSES,
  761. STATUS_INVALID_IMAGE_FORMAT);
  762. } except (EXCEPTION_EXECUTE_HANDLER) {
  763. Status = GetExceptionCode();
  764. KdPrint(("MM:sysload - LdrRelocateImage failed status %lx\n",
  765. Status));
  766. }
  767. if (!NT_SUCCESS(Status)) {
  768. //
  769. // Unload the system image and dereference the section.
  770. //
  771. goto return1;
  772. }
  773. }
  774. if (AlreadyOpen == FALSE) {
  775. ULONG DebugInfoSize;
  776. PIMAGE_DATA_DIRECTORY DataDirectory;
  777. PIMAGE_DEBUG_DIRECTORY DebugDir;
  778. PNON_PAGED_DEBUG_INFO ssHeader;
  779. UCHAR i;
  780. DebugInfoSize = 0;
  781. DataDirectory = NULL;
  782. DebugDir = NULL;
  783. NtHeaders = RtlImageNtHeader(*ImageBaseAddress);
  784. //
  785. // Create a loader table entry for this driver before resolving the
  786. // references so that any circular references can resolve properly.
  787. //
  788. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  789. DebugInfoSize = sizeof (NON_PAGED_DEBUG_INFO);
  790. if (IMAGE_DIRECTORY_ENTRY_DEBUG <
  791. NtHeaders->OptionalHeader.NumberOfRvaAndSizes) {
  792. DataDirectory = &NtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG];
  793. if (DataDirectory->VirtualAddress &&
  794. DataDirectory->Size &&
  795. (DataDirectory->VirtualAddress + DataDirectory->Size) <
  796. NtHeaders->OptionalHeader.SizeOfImage) {
  797. DebugDir = (PIMAGE_DEBUG_DIRECTORY)
  798. ((PUCHAR)(*ImageBaseAddress) +
  799. DataDirectory->VirtualAddress);
  800. DebugInfoSize += DataDirectory->Size;
  801. for (i = 0;
  802. i < DataDirectory->Size/sizeof(IMAGE_DEBUG_DIRECTORY);
  803. i += 1) {
  804. if ((DebugDir+i)->PointerToRawData &&
  805. (DebugDir+i)->PointerToRawData <
  806. NtHeaders->OptionalHeader.SizeOfImage &&
  807. ((DebugDir+i)->PointerToRawData +
  808. (DebugDir+i)->SizeOfData) <
  809. NtHeaders->OptionalHeader.SizeOfImage) {
  810. DebugInfoSize += (DebugDir+i)->SizeOfData;
  811. }
  812. }
  813. }
  814. DebugInfoSize = MI_ROUND_TO_SIZE(DebugInfoSize, sizeof(ULONG));
  815. }
  816. }
  817. DataTableEntrySize = sizeof (KLDR_DATA_TABLE_ENTRY) +
  818. DebugInfoSize +
  819. BaseName.Length + sizeof(UNICODE_NULL);
  820. DataTableEntry = ExAllocatePoolWithTag (NonPagedPool,
  821. DataTableEntrySize,
  822. 'dLmM');
  823. if (DataTableEntry == NULL) {
  824. Status = STATUS_INSUFFICIENT_RESOURCES;
  825. goto return1;
  826. }
  827. //
  828. // Initialize the flags and load count.
  829. //
  830. DataTableEntry->Flags = LDRP_LOAD_IN_PROGRESS;
  831. DataTableEntry->LoadCount = 1;
  832. DataTableEntry->LoadedImports = (PVOID)LoadedImports;
  833. if ((NtHeaders->OptionalHeader.MajorOperatingSystemVersion >= 5) &&
  834. (NtHeaders->OptionalHeader.MajorImageVersion >= 5)) {
  835. DataTableEntry->Flags |= LDRP_ENTRY_NATIVE;
  836. }
  837. ssHeader = (PNON_PAGED_DEBUG_INFO) ((ULONG_PTR)DataTableEntry +
  838. sizeof (KLDR_DATA_TABLE_ENTRY));
  839. BaseDllNameBuffer = (PWSTR) ((ULONG_PTR)ssHeader + DebugInfoSize);
  840. //
  841. // If loading a session space image, store away some debug data.
  842. //
  843. DataTableEntry->NonPagedDebugInfo = NULL;
  844. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  845. DataTableEntry->NonPagedDebugInfo = ssHeader;
  846. DataTableEntry->Flags |= LDRP_NON_PAGED_DEBUG_INFO;
  847. ssHeader->Signature = NON_PAGED_DEBUG_SIGNATURE;
  848. ssHeader->Flags = 1;
  849. ssHeader->Size = DebugInfoSize;
  850. ssHeader->Machine = NtHeaders->FileHeader.Machine;
  851. ssHeader->Characteristics = NtHeaders->FileHeader.Characteristics;
  852. ssHeader->TimeDateStamp = NtHeaders->FileHeader.TimeDateStamp;
  853. ssHeader->CheckSum = NtHeaders->OptionalHeader.CheckSum;
  854. ssHeader->SizeOfImage = NtHeaders->OptionalHeader.SizeOfImage;
  855. ssHeader->ImageBase = (ULONG_PTR) *ImageBaseAddress;
  856. if (DebugDir)
  857. {
  858. RtlCopyMemory(ssHeader + 1,
  859. DebugDir,
  860. DataDirectory->Size);
  861. DebugInfoSize = DataDirectory->Size;
  862. for (i = 0;
  863. i < DataDirectory->Size/sizeof(IMAGE_DEBUG_DIRECTORY);
  864. i += 1) {
  865. if ((DebugDir + i)->PointerToRawData &&
  866. (DebugDir+i)->PointerToRawData <
  867. NtHeaders->OptionalHeader.SizeOfImage &&
  868. ((DebugDir+i)->PointerToRawData +
  869. (DebugDir+i)->SizeOfData) <
  870. NtHeaders->OptionalHeader.SizeOfImage) {
  871. RtlCopyMemory((PUCHAR)(ssHeader + 1) +
  872. DebugInfoSize,
  873. (PUCHAR)(*ImageBaseAddress) +
  874. (DebugDir + i)->PointerToRawData,
  875. (DebugDir + i)->SizeOfData);
  876. //
  877. // Reset the offset in the debug directory to point to
  878. //
  879. (((PIMAGE_DEBUG_DIRECTORY)(ssHeader + 1)) + i)->
  880. PointerToRawData = DebugInfoSize;
  881. DebugInfoSize += (DebugDir+i)->SizeOfData;
  882. }
  883. else
  884. {
  885. (((PIMAGE_DEBUG_DIRECTORY)(ssHeader + 1)) + i)->
  886. PointerToRawData = 0;
  887. }
  888. }
  889. }
  890. }
  891. //
  892. // Initialize the address of the DLL image file header and the entry
  893. // point address.
  894. //
  895. DataTableEntry->DllBase = *ImageBaseAddress;
  896. DataTableEntry->EntryPoint =
  897. ((PCHAR)*ImageBaseAddress + NtHeaders->OptionalHeader.AddressOfEntryPoint);
  898. DataTableEntry->SizeOfImage = NumberOfPtes << PAGE_SHIFT;
  899. DataTableEntry->CheckSum = NtHeaders->OptionalHeader.CheckSum;
  900. DataTableEntry->SectionPointer = (PVOID)SectionPointer;
  901. //
  902. // Store the DLL name.
  903. //
  904. DataTableEntry->BaseDllName.Buffer = BaseDllNameBuffer;
  905. DataTableEntry->BaseDllName.Length = BaseName.Length;
  906. DataTableEntry->BaseDllName.MaximumLength = BaseName.Length;
  907. RtlCopyMemory (DataTableEntry->BaseDllName.Buffer,
  908. BaseName.Buffer,
  909. BaseName.Length );
  910. DataTableEntry->BaseDllName.Buffer[BaseName.Length/sizeof(WCHAR)] = UNICODE_NULL;
  911. DataTableEntry->FullDllName.Buffer = ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  912. PrefixedImageName.Length + sizeof(UNICODE_NULL),
  913. 'TDmM');
  914. if (DataTableEntry->FullDllName.Buffer == NULL) {
  915. //
  916. // Pool could not be allocated, just set the length to 0.
  917. //
  918. DataTableEntry->FullDllName.Length = 0;
  919. DataTableEntry->FullDllName.MaximumLength = 0;
  920. }
  921. else {
  922. DataTableEntry->FullDllName.Length = PrefixedImageName.Length;
  923. DataTableEntry->FullDllName.MaximumLength = PrefixedImageName.Length;
  924. RtlCopyMemory (DataTableEntry->FullDllName.Buffer,
  925. PrefixedImageName.Buffer,
  926. PrefixedImageName.Length);
  927. DataTableEntry->FullDllName.Buffer[PrefixedImageName.Length/sizeof(WCHAR)] = UNICODE_NULL;
  928. }
  929. //
  930. // Acquire the loaded module list resource and insert this entry
  931. // into the list.
  932. //
  933. MiProcessLoaderEntry (DataTableEntry, TRUE);
  934. }
  935. MissingProcedureName = NameBuffer;
  936. try {
  937. //
  938. // Resolving the image references results in other DLLs being
  939. // loaded if they are referenced by the module that was just loaded.
  940. // An example is when an OEM printer or FAX driver links with
  941. // other general libraries. This is not a problem for session space
  942. // because the general libraries do not have the global data issues
  943. // that win32k.sys and the video drivers do. So we just call the
  944. // standard kernel reference resolver and any referenced libraries
  945. // get loaded into system global space. Code in the routine
  946. // restricts which libraries can be referenced by a driver.
  947. //
  948. Status = MiResolveImageReferences (*ImageBaseAddress,
  949. &BaseDirectory,
  950. NamePrefix,
  951. &MissingProcedureName,
  952. &MissingDriverName,
  953. &LoadedImports);
  954. } except (EXCEPTION_EXECUTE_HANDLER) {
  955. Status = GetExceptionCode ();
  956. KdPrint(("MM:sysload - ResolveImageReferences failed status %x\n",
  957. Status));
  958. }
  959. if (!NT_SUCCESS(Status)) {
  960. #if DBG
  961. if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
  962. ASSERT (MissingProcedureName == NULL);
  963. }
  964. if ((Status == STATUS_DRIVER_ORDINAL_NOT_FOUND) ||
  965. (Status == STATUS_OBJECT_NAME_NOT_FOUND) ||
  966. (Status == STATUS_DRIVER_ENTRYPOINT_NOT_FOUND)) {
  967. if ((ULONG_PTR)MissingProcedureName & ~((ULONG_PTR) (X64K-1))) {
  968. //
  969. // If not an ordinal, print string
  970. //
  971. DbgPrint("MissingProcedureName %s\n", MissingProcedureName);
  972. }
  973. else {
  974. DbgPrint("MissingProcedureName 0x%p\n", MissingProcedureName);
  975. }
  976. }
  977. if (MissingDriverName != NULL) {
  978. PrintableMissingDriverName = (PWSTR)((ULONG_PTR)MissingDriverName & ~0x1);
  979. DbgPrint("MissingDriverName %ws\n", PrintableMissingDriverName);
  980. }
  981. #endif
  982. if (AlreadyOpen == FALSE) {
  983. MiProcessLoaderEntry (DataTableEntry, FALSE);
  984. if (DataTableEntry->FullDllName.Buffer != NULL) {
  985. ExFreePool (DataTableEntry->FullDllName.Buffer);
  986. }
  987. ExFreePool (DataTableEntry);
  988. DataTableEntry = NULL;
  989. }
  990. goto return1;
  991. }
  992. if (AlreadyOpen == FALSE) {
  993. PERFINFO_IMAGE_LOAD(DataTableEntry);
  994. //
  995. // Reinitialize the flags and update the loaded imports.
  996. //
  997. DataTableEntry->Flags |= LDRP_SYSTEM_MAPPED | LDRP_ENTRY_PROCESSED;
  998. DataTableEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
  999. DataTableEntry->LoadedImports = LoadedImports;
  1000. MiApplyDriverVerifier (DataTableEntry, NULL);
  1001. MiWriteProtectSystemImage (DataTableEntry->DllBase);
  1002. if (PsImageNotifyEnabled) {
  1003. IMAGE_INFO ImageInfo;
  1004. ImageInfo.Properties = 0;
  1005. ImageInfo.ImageAddressingMode = IMAGE_ADDRESSING_MODE_32BIT;
  1006. ImageInfo.SystemModeImage = TRUE;
  1007. ImageInfo.ImageSize = DataTableEntry->SizeOfImage;
  1008. ImageInfo.ImageBase = *ImageBaseAddress;
  1009. ImageInfo.ImageSelector = 0;
  1010. ImageInfo.ImageSectionNumber = 0;
  1011. PsCallImageNotifyRoutines(ImageFileName, (HANDLE)NULL, &ImageInfo);
  1012. }
  1013. if (CacheImageSymbols (*ImageBaseAddress)) {
  1014. //
  1015. // TEMP TEMP TEMP rip out when debugger converted
  1016. //
  1017. ANSI_STRING AnsiName;
  1018. UNICODE_STRING UnicodeName;
  1019. //
  1020. // \SystemRoot is 11 characters in length
  1021. //
  1022. if (PrefixedImageName.Length > (11 * sizeof (WCHAR )) &&
  1023. !_wcsnicmp (PrefixedImageName.Buffer, (const PUSHORT)L"\\SystemRoot", 11)) {
  1024. UnicodeName = PrefixedImageName;
  1025. UnicodeName.Buffer += 11;
  1026. UnicodeName.Length -= (11 * sizeof (WCHAR));
  1027. sprintf (NameBuffer, "%ws%wZ", &SharedUserData->NtSystemRoot[2], &UnicodeName);
  1028. }
  1029. else {
  1030. sprintf (NameBuffer, "%wZ", &BaseName);
  1031. }
  1032. RtlInitString (&AnsiName, NameBuffer);
  1033. DbgLoadImageSymbols (&AnsiName,
  1034. *ImageBaseAddress,
  1035. (ULONG_PTR) -1);
  1036. DataTableEntry->Flags |= LDRP_DEBUG_SYMBOLS_LOADED;
  1037. }
  1038. }
  1039. //
  1040. // Flush the instruction cache on all systems in the configuration.
  1041. //
  1042. KeSweepIcache (TRUE);
  1043. *ImageHandle = DataTableEntry;
  1044. Status = STATUS_SUCCESS;
  1045. if (LoadFlags & MM_LOAD_IMAGE_IN_SESSION) {
  1046. MI_LOG_SESSION_DATA_START (DataTableEntry);
  1047. MmPageEntireDriver (DataTableEntry->EntryPoint);
  1048. }
  1049. else if ((SectionPointer == (PVOID)-1) &&
  1050. ((LoadFlags & MM_LOAD_IMAGE_AND_LOCKDOWN) == 0)) {
  1051. MiEnablePagingOfDriver (DataTableEntry);
  1052. }
  1053. return1:
  1054. if (!NT_SUCCESS(Status)) {
  1055. if ((AlreadyOpen == FALSE) && (SectionPointer != (PVOID)-1)) {
  1056. //
  1057. // This is needed for failed win32k.sys loads or any session's
  1058. // load of the first instance of a driver.
  1059. //
  1060. ObDereferenceObject (SectionPointer);
  1061. }
  1062. if (IssueUnloadOnFailure == TRUE) {
  1063. if (DataTableEntry == NULL) {
  1064. RtlZeroMemory (&TempDataTableEntry, sizeof(KLDR_DATA_TABLE_ENTRY));
  1065. DataTableEntry = &TempDataTableEntry;
  1066. DataTableEntry->DllBase = *ImageBaseAddress;
  1067. DataTableEntry->SizeOfImage = NumberOfPtes << PAGE_SHIFT;
  1068. DataTableEntry->LoadCount = 1;
  1069. DataTableEntry->LoadedImports = LoadedImports;
  1070. }
  1071. #if DBG
  1072. else {
  1073. //
  1074. // If DataTableEntry is NULL, then we are unloading before one
  1075. // got created. Once a LDR_DATA_TABLE_ENTRY is created, the
  1076. // load cannot fail, so if exists here, at least one other
  1077. // session contains this image as well.
  1078. //
  1079. ASSERT (DataTableEntry->LoadCount > 1);
  1080. }
  1081. #endif
  1082. MmUnloadSystemImage ((PVOID)DataTableEntry);
  1083. }
  1084. }
  1085. if (FileHandle) {
  1086. ZwClose (FileHandle);
  1087. }
  1088. if (!NT_SUCCESS(Status)) {
  1089. UNICODE_STRING ErrorStrings[4];
  1090. ULONG UniqueErrorValue;
  1091. ULONG StringSize;
  1092. ULONG StringCount;
  1093. ANSI_STRING AnsiString;
  1094. UNICODE_STRING ProcedureName;
  1095. UNICODE_STRING DriverName;
  1096. ULONG i;
  1097. PWCHAR temp;
  1098. PWCHAR ptr;
  1099. ULONG PacketSize;
  1100. SIZE_T length;
  1101. PIO_ERROR_LOG_PACKET ErrLog;
  1102. //
  1103. // The driver could not be loaded - log an event with the details.
  1104. //
  1105. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  1106. KeLeaveCriticalRegionThread (CurrentThread);
  1107. StringSize = 0;
  1108. *(&ErrorStrings[0]) = *ImageFileName;
  1109. StringSize += (ImageFileName->Length + sizeof(UNICODE_NULL));
  1110. StringCount = 1;
  1111. UniqueErrorValue = 0;
  1112. RtlInitUnicodeString (&ProcedureName, NULL);
  1113. PrintableMissingDriverName = (PWSTR)((ULONG_PTR)MissingDriverName & ~0x1);
  1114. if ((Status == STATUS_DRIVER_ORDINAL_NOT_FOUND) ||
  1115. (Status == STATUS_DRIVER_ENTRYPOINT_NOT_FOUND) ||
  1116. (Status == STATUS_OBJECT_NAME_NOT_FOUND) ||
  1117. (Status == STATUS_PROCEDURE_NOT_FOUND)) {
  1118. ErrorStrings[1].Buffer = L"cannot find";
  1119. length = wcslen(ErrorStrings[1].Buffer) * sizeof(WCHAR);
  1120. ErrorStrings[1].Length = (USHORT) length;
  1121. StringSize += (ULONG)(length + sizeof (UNICODE_NULL));
  1122. StringCount += 1;
  1123. RtlInitUnicodeString (&DriverName, PrintableMissingDriverName);
  1124. StringSize += (DriverName.Length + sizeof(UNICODE_NULL));
  1125. StringCount += 1;
  1126. *(&ErrorStrings[2]) = *(&DriverName);
  1127. if ((ULONG_PTR)MissingProcedureName & ~((ULONG_PTR) (X64K-1))) {
  1128. //
  1129. // If not an ordinal, pass as a Unicode string
  1130. //
  1131. RtlInitAnsiString (&AnsiString, MissingProcedureName);
  1132. RtlAnsiStringToUnicodeString (&ProcedureName, &AnsiString, TRUE);
  1133. StringSize += (ProcedureName.Length + sizeof(UNICODE_NULL));
  1134. StringCount += 1;
  1135. *(&ErrorStrings[3]) = *(&ProcedureName);
  1136. }
  1137. else {
  1138. //
  1139. // Just pass ordinal values as is in the UniqueErrorValue.
  1140. //
  1141. UniqueErrorValue = PtrToUlong (MissingProcedureName);
  1142. }
  1143. }
  1144. else {
  1145. UniqueErrorValue = (ULONG) Status;
  1146. Status = STATUS_DRIVER_UNABLE_TO_LOAD;
  1147. ErrorStrings[1].Buffer = L"failed to load";
  1148. length = wcslen(ErrorStrings[1].Buffer) * sizeof(WCHAR);
  1149. ErrorStrings[1].Length = (USHORT) length;
  1150. StringSize += (ULONG)(length + sizeof (UNICODE_NULL));
  1151. StringCount += 1;
  1152. }
  1153. PacketSize = sizeof (IO_ERROR_LOG_PACKET) + StringSize;
  1154. //
  1155. // Enforce I/O manager interface (ie: UCHAR) size restrictions.
  1156. //
  1157. if (PacketSize < MAXUCHAR) {
  1158. ErrLog = IoAllocateGenericErrorLogEntry ((UCHAR)PacketSize);
  1159. if (ErrLog != NULL) {
  1160. //
  1161. // Fill it in and write it out as a single string.
  1162. //
  1163. ErrLog->ErrorCode = STATUS_LOG_HARD_ERROR;
  1164. ErrLog->FinalStatus = Status;
  1165. ErrLog->UniqueErrorValue = UniqueErrorValue;
  1166. ErrLog->StringOffset = (USHORT) sizeof (IO_ERROR_LOG_PACKET);
  1167. temp = (PWCHAR) ((PUCHAR) ErrLog + ErrLog->StringOffset);
  1168. for (i = 0; i < StringCount; i += 1) {
  1169. ptr = ErrorStrings[i].Buffer;
  1170. RtlCopyMemory (temp, ptr, ErrorStrings[i].Length);
  1171. temp += (ErrorStrings[i].Length / sizeof (WCHAR));
  1172. *temp = L' ';
  1173. temp += 1;
  1174. }
  1175. *(temp - 1) = UNICODE_NULL;
  1176. ErrLog->NumberOfStrings = 1;
  1177. IoWriteErrorLogEntry (ErrLog);
  1178. }
  1179. }
  1180. //
  1181. // The only way this pointer has the low bit set is if we are expected
  1182. // to free the pool containing the name. Typically the name points at
  1183. // a loaded module list entry and so no one has to free it and in this
  1184. // case the low bit will NOT be set. If the module could not be found
  1185. // and was therefore not loaded, then we left a piece of pool around
  1186. // containing the name since there is no loaded module entry already -
  1187. // this must be released now.
  1188. //
  1189. if ((ULONG_PTR)MissingDriverName & 0x1) {
  1190. ExFreePool (PrintableMissingDriverName);
  1191. }
  1192. if (ProcedureName.Buffer != NULL) {
  1193. RtlFreeUnicodeString (&ProcedureName);
  1194. }
  1195. ExFreePool (NameBuffer);
  1196. return Status;
  1197. }
  1198. return2:
  1199. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  1200. KeLeaveCriticalRegionThread (CurrentThread);
  1201. if (NamePrefix) {
  1202. ExFreePool (PrefixedImageName.Buffer);
  1203. }
  1204. ExFreePool (NameBuffer);
  1205. return Status;
  1206. }
  1207. VOID
  1208. MiReturnFailedSessionPages (
  1209. IN PMMPTE PointerPte,
  1210. IN PMMPTE LastPte,
  1211. IN PFN_NUMBER NumberOfPages
  1212. )
  1213. /*++
  1214. Routine Description:
  1215. This routine is a nonpaged wrapper which undoes session image loads
  1216. that failed midway through reading in the pages.
  1217. Arguments:
  1218. PointerPte - Supplies the starting PTE for the range to unload.
  1219. LastPte - Supplies the ending PTE for the range to unload.
  1220. NumberOfPages - Supplies the number of resident available pages that
  1221. were charged and now need to be returned.
  1222. Return Value:
  1223. None.
  1224. --*/
  1225. {
  1226. KIRQL OldIrql;
  1227. PMMPFN Pfn1;
  1228. PFN_NUMBER PageFrameIndex;
  1229. LOCK_PFN (OldIrql);
  1230. while (PointerPte <= LastPte) {
  1231. if (PointerPte->u.Hard.Valid == 1) {
  1232. //
  1233. // Delete the page.
  1234. //
  1235. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  1236. //
  1237. // Set the pointer to PTE as empty so the page
  1238. // is deleted when the reference count goes to zero.
  1239. //
  1240. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  1241. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  1242. MI_SET_PFN_DELETED (Pfn1);
  1243. MiDecrementShareCountOnly (PageFrameIndex);
  1244. MI_WRITE_INVALID_PTE (PointerPte, ZeroPte);
  1245. }
  1246. PointerPte += 1;
  1247. }
  1248. MmResidentAvailablePages += NumberOfPages;
  1249. MM_BUMP_COUNTER(17, NumberOfPages);
  1250. UNLOCK_PFN (OldIrql);
  1251. }
  1252. NTSTATUS
  1253. MiLoadImageSection (
  1254. IN PSECTION SectionPointer,
  1255. OUT PVOID *ImageBaseAddress,
  1256. IN PUNICODE_STRING ImageFileName,
  1257. IN ULONG LoadInSessionSpace
  1258. )
  1259. /*++
  1260. Routine Description:
  1261. This routine loads the specified image into the kernel part of the
  1262. address space.
  1263. Arguments:
  1264. SectionPointer - Supplies the section object for the image.
  1265. ImageBaseAddress - Returns the address that the image header is at.
  1266. ImageFileName - Supplies the full path name (including the image name)
  1267. of the image to load.
  1268. LoadInSessionSpace - Supplies nonzero to load this image in session space.
  1269. Each session gets a different copy of this driver with
  1270. pages shared as much as possible via copy on write.
  1271. Supplies zero if this image should be loaded in global
  1272. space.
  1273. Return Value:
  1274. Status of the operation.
  1275. --*/
  1276. {
  1277. KAPC_STATE ApcState;
  1278. PFN_NUMBER PagesRequired;
  1279. PFN_NUMBER ActualPagesUsed;
  1280. PVOID OpaqueSession;
  1281. PMMPTE ProtoPte;
  1282. PMMPTE FirstPte;
  1283. PMMPTE LastPte;
  1284. PMMPTE PointerPte;
  1285. PEPROCESS Process;
  1286. PEPROCESS TargetProcess;
  1287. ULONG NumberOfPtes;
  1288. MMPTE PteContents;
  1289. MMPTE TempPte;
  1290. PFN_NUMBER PageFrameIndex;
  1291. PVOID UserVa;
  1292. PVOID SystemVa;
  1293. NTSTATUS Status;
  1294. NTSTATUS ExceptionStatus;
  1295. PVOID Base;
  1296. ULONG_PTR ViewSize;
  1297. LARGE_INTEGER SectionOffset;
  1298. LOGICAL LoadSymbols;
  1299. PVOID BaseAddress;
  1300. PFN_NUMBER CommittedPages;
  1301. SIZE_T SectionSize;
  1302. LOGICAL AlreadyLoaded;
  1303. PCONTROL_AREA ControlArea;
  1304. PSUBSECTION Subsection;
  1305. PAGED_CODE();
  1306. NumberOfPtes = SectionPointer->Segment->TotalNumberOfPtes;
  1307. if (LoadInSessionSpace != 0) {
  1308. SectionSize = (ULONG_PTR)NumberOfPtes * PAGE_SIZE;
  1309. //
  1310. // Allocate a unique systemwide session space virtual address for
  1311. // the driver.
  1312. //
  1313. Status = MiSessionWideReserveImageAddress (ImageFileName,
  1314. SectionPointer,
  1315. PAGE_SIZE,
  1316. &BaseAddress,
  1317. &AlreadyLoaded);
  1318. if (!NT_SUCCESS(Status)) {
  1319. return Status;
  1320. }
  1321. //
  1322. // This is a request to load an existing driver. This can
  1323. // occur with printer drivers for example.
  1324. //
  1325. if (AlreadyLoaded == TRUE) {
  1326. *ImageBaseAddress = BaseAddress;
  1327. return STATUS_ALREADY_COMMITTED;
  1328. }
  1329. #if DBG
  1330. if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
  1331. DbgPrint ("MM: MiLoadImageSection: Image %wZ, BasedAddress 0x%p, Allocated Session BaseAddress 0x%p\n",
  1332. ImageFileName,
  1333. SectionPointer->Segment->BasedAddress,
  1334. BaseAddress);
  1335. }
  1336. #endif
  1337. if (BaseAddress == SectionPointer->Segment->BasedAddress) {
  1338. //
  1339. // We were able to load the image at its based address, so
  1340. // map its image segments as backed directly by the file image.
  1341. // All pristine pages of the image will be shared across all
  1342. // sessions, with each page treated as copy-on-write on first write.
  1343. //
  1344. // NOTE: This makes the file image "busy", a different behavior
  1345. // as normal kernel drivers are backed by the paging file only.
  1346. //
  1347. // Map the image into session space.
  1348. //
  1349. Status = MiShareSessionImage (SectionPointer, &SectionSize);
  1350. if (!NT_SUCCESS(Status)) {
  1351. MiRemoveImageSessionWide (BaseAddress);
  1352. return Status;
  1353. }
  1354. ASSERT (BaseAddress == SectionPointer->Segment->BasedAddress);
  1355. *ImageBaseAddress = BaseAddress;
  1356. //
  1357. // Indicate that this section has been loaded into the system.
  1358. //
  1359. SectionPointer->Segment->SystemImageBase = BaseAddress;
  1360. #if DBG
  1361. if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
  1362. DbgPrint ("MM: MiLoadImageSection: Mapped image %wZ at requested session address 0x%p\n",
  1363. ImageFileName,
  1364. BaseAddress);
  1365. }
  1366. #endif
  1367. return Status;
  1368. }
  1369. //
  1370. // The image could not be loaded at its based address. It must be
  1371. // copied to its new address using private page file backed pages.
  1372. // Our caller will relocate the internal references and then bind the
  1373. // image. Allocate the pages and page tables for the image now.
  1374. //
  1375. Status = MiSessionCommitImagePages (BaseAddress, SectionSize);
  1376. if (!NT_SUCCESS(Status)) {
  1377. #if DBG
  1378. if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
  1379. DbgPrint ("MM: MiLoadImageSection: Error 0x%x Allocating session space %p Bytes\n", Status, SectionSize);
  1380. }
  1381. #endif
  1382. MiRemoveImageSessionWide (BaseAddress);
  1383. return Status;
  1384. }
  1385. SystemVa = BaseAddress;
  1386. //
  1387. // Initializing these is not needed for correctness, but
  1388. // without it the compiler cannot compile this code W4 to check
  1389. // for use of uninitialized variables.
  1390. //
  1391. PagesRequired = 0;
  1392. ActualPagesUsed = 0;
  1393. PointerPte = NULL;
  1394. FirstPte = NULL;
  1395. }
  1396. else {
  1397. //
  1398. // Initializing SectionSize and BaseAddress is not needed for
  1399. // correctness, but without it the compiler cannot compile this
  1400. // code W4 to check for use of uninitialized variables.
  1401. //
  1402. SectionSize = 0;
  1403. BaseAddress = NULL;
  1404. //
  1405. // Calculate the number of pages required to load this image.
  1406. //
  1407. // Start out by charging for everything and subtract out any gap
  1408. // pages after the image loads successfully.
  1409. //
  1410. PagesRequired = NumberOfPtes;
  1411. ActualPagesUsed = 0;
  1412. //
  1413. // See if ample pages exist to load this image.
  1414. //
  1415. if (MiChargeResidentAvailable (PagesRequired, 14) == FALSE) {
  1416. return STATUS_INSUFFICIENT_RESOURCES;
  1417. }
  1418. //
  1419. // Reserve the necessary system address space.
  1420. //
  1421. FirstPte = MiReserveSystemPtes (NumberOfPtes, SystemPteSpace);
  1422. if (FirstPte == NULL) {
  1423. MiReturnResidentAvailable (PagesRequired, 15);
  1424. return STATUS_INSUFFICIENT_RESOURCES;
  1425. }
  1426. PointerPte = FirstPte;
  1427. SystemVa = MiGetVirtualAddressMappedByPte (PointerPte);
  1428. if (MiChargeCommitment (PagesRequired, NULL) == FALSE) {
  1429. MiReturnResidentAvailable (PagesRequired, 15);
  1430. MiReleaseSystemPtes (FirstPte, NumberOfPtes, SystemPteSpace);
  1431. return STATUS_INSUFFICIENT_RESOURCES;
  1432. }
  1433. MM_TRACK_COMMIT (MM_DBG_COMMIT_DRIVER_PAGES, PagesRequired);
  1434. InterlockedExchangeAdd ((PLONG)&MmDriverCommit, (LONG) PagesRequired);
  1435. }
  1436. //
  1437. // Map a view into the user portion of the address space.
  1438. //
  1439. Process = PsGetCurrentProcess();
  1440. //
  1441. // Since callees are not always in the context of the system process,
  1442. // attach here when necessary to guarantee the driver load occurs in a
  1443. // known safe address space to prevent security holes.
  1444. //
  1445. OpaqueSession = NULL;
  1446. if ((Process->Peb != NULL) &&
  1447. (Process->Vm.Flags.SessionLeader == 0) &&
  1448. (MiSessionLeaderExists == 2)) {
  1449. OpaqueSession = MiAttachToSecureProcessInSession (&ApcState);
  1450. if (OpaqueSession == NULL) {
  1451. if (LoadInSessionSpace != 0) {
  1452. CommittedPages = MiDeleteSystemPagableVm (
  1453. MiGetPteAddress (BaseAddress),
  1454. BYTES_TO_PAGES (SectionSize),
  1455. ZeroKernelPte,
  1456. TRUE,
  1457. NULL);
  1458. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  1459. 0 - CommittedPages);
  1460. MM_BUMP_SESS_COUNTER(MM_DBG_SESSION_COMMIT_IMAGELOAD_FAILED1,
  1461. (ULONG)CommittedPages);
  1462. //
  1463. // Return the commitment we took out on the pagefile when
  1464. // the page was allocated. This is needed for collided images
  1465. // since all the pages get committed regardless of writability.
  1466. //
  1467. MiRemoveImageSessionWide (BaseAddress);
  1468. }
  1469. else {
  1470. MiReturnResidentAvailable (PagesRequired, 15);
  1471. MiReleaseSystemPtes (FirstPte, NumberOfPtes, SystemPteSpace);
  1472. MiReturnCommitment (PagesRequired);
  1473. }
  1474. return STATUS_PROCESS_IS_TERMINATING;
  1475. }
  1476. //
  1477. // We are now attached to a secure process in the current session.
  1478. //
  1479. }
  1480. ZERO_LARGE (SectionOffset);
  1481. Base = NULL;
  1482. ViewSize = 0;
  1483. if (NtGlobalFlag & FLG_ENABLE_KDEBUG_SYMBOL_LOAD) {
  1484. LoadSymbols = TRUE;
  1485. NtGlobalFlag &= ~FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
  1486. }
  1487. else {
  1488. LoadSymbols = FALSE;
  1489. }
  1490. TargetProcess = PsGetCurrentProcess ();
  1491. Status = MmMapViewOfSection (SectionPointer,
  1492. TargetProcess,
  1493. &Base,
  1494. 0,
  1495. 0,
  1496. &SectionOffset,
  1497. &ViewSize,
  1498. ViewUnmap,
  1499. 0,
  1500. PAGE_EXECUTE);
  1501. if (LoadSymbols) {
  1502. NtGlobalFlag |= FLG_ENABLE_KDEBUG_SYMBOL_LOAD;
  1503. }
  1504. if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) {
  1505. Status = STATUS_INVALID_IMAGE_FORMAT;
  1506. }
  1507. if (!NT_SUCCESS(Status)) {
  1508. if (OpaqueSession != NULL) {
  1509. MiDetachFromSecureProcessInSession (OpaqueSession, &ApcState);
  1510. }
  1511. if (LoadInSessionSpace != 0) {
  1512. #if DBG
  1513. if (NtGlobalFlag & FLG_SHOW_LDR_SNAPS) {
  1514. DbgPrint ("MiLoadImageSection: Error 0x%x in session space mapping via MmMapViewOfSection\n", Status);
  1515. }
  1516. #endif
  1517. CommittedPages = MiDeleteSystemPagableVm (
  1518. MiGetPteAddress (BaseAddress),
  1519. BYTES_TO_PAGES (SectionSize),
  1520. ZeroKernelPte,
  1521. TRUE,
  1522. NULL);
  1523. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  1524. 0 - CommittedPages);
  1525. MM_BUMP_SESS_COUNTER(MM_DBG_SESSION_COMMIT_IMAGELOAD_FAILED1,
  1526. (ULONG)CommittedPages);
  1527. //
  1528. // Return the commitment we took out on the pagefile when
  1529. // the page was allocated. This is needed for collided images
  1530. // since all the pages get committed regardless of writability.
  1531. //
  1532. MiRemoveImageSessionWide (BaseAddress);
  1533. }
  1534. else {
  1535. MiReturnResidentAvailable (PagesRequired, 16);
  1536. MiReleaseSystemPtes (FirstPte, NumberOfPtes, SystemPteSpace);
  1537. MiReturnCommitment (PagesRequired);
  1538. }
  1539. return Status;
  1540. }
  1541. //
  1542. // Allocate a physical page(s) and copy the image data.
  1543. // Note Hydra has already allocated the physical pages and just does
  1544. // data copying here.
  1545. //
  1546. ControlArea = SectionPointer->Segment->ControlArea;
  1547. if ((ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  1548. (ControlArea->u.Flags.Rom == 0)) {
  1549. Subsection = (PSUBSECTION)(ControlArea + 1);
  1550. }
  1551. else {
  1552. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)ControlArea + 1);
  1553. }
  1554. ASSERT (Subsection->SubsectionBase != NULL);
  1555. ProtoPte = Subsection->SubsectionBase;
  1556. *ImageBaseAddress = SystemVa;
  1557. UserVa = Base;
  1558. TempPte = ValidKernelPte;
  1559. TempPte.u.Long |= MM_PTE_EXECUTE;
  1560. LastPte = ProtoPte + NumberOfPtes;
  1561. ExceptionStatus = STATUS_SUCCESS;
  1562. while (ProtoPte < LastPte) {
  1563. PteContents = *ProtoPte;
  1564. if ((PteContents.u.Hard.Valid == 1) ||
  1565. (PteContents.u.Soft.Protection != MM_NOACCESS)) {
  1566. if (LoadInSessionSpace == 0) {
  1567. ActualPagesUsed += 1;
  1568. PageFrameIndex = MiAllocatePfn (PointerPte, MM_EXECUTE);
  1569. TempPte.u.Hard.PageFrameNumber = PageFrameIndex;
  1570. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  1571. ASSERT (MI_PFN_ELEMENT (PageFrameIndex)->u1.WsIndex == 0);
  1572. }
  1573. try {
  1574. RtlCopyMemory (SystemVa, UserVa, PAGE_SIZE);
  1575. } except (MiMapCacheExceptionFilter (&ExceptionStatus,
  1576. GetExceptionInformation())) {
  1577. //
  1578. // An exception occurred, unmap the view and
  1579. // return the error to the caller.
  1580. //
  1581. #if DBG
  1582. DbgPrint("MiLoadImageSection: Exception 0x%x copying driver SystemVa 0x%p, UserVa 0x%p\n",ExceptionStatus,SystemVa,UserVa);
  1583. #endif
  1584. if (LoadInSessionSpace != 0) {
  1585. CommittedPages = MiDeleteSystemPagableVm (
  1586. MiGetPteAddress (BaseAddress),
  1587. BYTES_TO_PAGES (SectionSize),
  1588. ZeroKernelPte,
  1589. TRUE,
  1590. NULL);
  1591. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  1592. 0 - CommittedPages);
  1593. MM_BUMP_SESS_COUNTER(MM_DBG_SESSION_COMMIT_IMAGELOAD_FAILED2,
  1594. (ULONG)CommittedPages);
  1595. //
  1596. // Return the commitment we took out on the pagefile when
  1597. // the page was allocated. This is needed for collided
  1598. // images since all the pages get committed regardless
  1599. // of writability.
  1600. //
  1601. MiReturnCommitment (CommittedPages);
  1602. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_SESSION_DRIVER_LOAD_FAILURE1, CommittedPages);
  1603. }
  1604. else {
  1605. MiReturnFailedSessionPages (FirstPte, PointerPte, PagesRequired);
  1606. MiReleaseSystemPtes (FirstPte,
  1607. NumberOfPtes,
  1608. SystemPteSpace);
  1609. MiReturnCommitment (PagesRequired);
  1610. }
  1611. Status = MiUnmapViewOfSection (TargetProcess, Base, FALSE);
  1612. ASSERT (NT_SUCCESS (Status));
  1613. //
  1614. // Purge the section as we want these pages on the freelist
  1615. // instead of at the tail of standby, as we're completely
  1616. // done with the section. This is because other valuable
  1617. // standby pages end up getting reused (especially during
  1618. // bootup) when the section pages are the ones that really
  1619. // will never be referenced again.
  1620. //
  1621. // Note this isn't done for session images as they're
  1622. // inpaged directly from the filesystem via the section.
  1623. //
  1624. if (LoadInSessionSpace == 0) {
  1625. MmPurgeSection (ControlArea->FilePointer->SectionObjectPointer,
  1626. NULL,
  1627. 0,
  1628. FALSE);
  1629. }
  1630. if (OpaqueSession != NULL) {
  1631. MiDetachFromSecureProcessInSession (OpaqueSession,
  1632. &ApcState);
  1633. }
  1634. if (LoadInSessionSpace != 0) {
  1635. MiRemoveImageSessionWide (BaseAddress);
  1636. }
  1637. return ExceptionStatus;
  1638. }
  1639. }
  1640. else {
  1641. //
  1642. // The PTE is no access - if this driver is being loaded in session
  1643. // space we already preloaded the page so free it now. The
  1644. // commitment is returned when the whole image is unmapped.
  1645. //
  1646. if (LoadInSessionSpace != 0) {
  1647. CommittedPages = MiDeleteSystemPagableVm (
  1648. MiGetPteAddress (SystemVa),
  1649. 1,
  1650. ZeroKernelPte,
  1651. TRUE,
  1652. NULL);
  1653. MM_BUMP_SESS_COUNTER(MM_DBG_SESSION_COMMIT_IMAGELOAD_NOACCESS,
  1654. 1);
  1655. }
  1656. else {
  1657. MI_WRITE_INVALID_PTE (PointerPte, ZeroKernelPte);
  1658. }
  1659. }
  1660. ProtoPte += 1;
  1661. if (LoadInSessionSpace == 0) {
  1662. PointerPte += 1;
  1663. }
  1664. SystemVa = ((PCHAR)SystemVa + PAGE_SIZE);
  1665. UserVa = ((PCHAR)UserVa + PAGE_SIZE);
  1666. }
  1667. Status = MiUnmapViewOfSection (TargetProcess, Base, FALSE);
  1668. ASSERT (NT_SUCCESS (Status));
  1669. //
  1670. // Purge the section as we want these pages on the freelist instead of
  1671. // at the tail of standby, as we're completely done with the section.
  1672. // This is because other valuable standby pages end up getting reused
  1673. // (especially during bootup) when the section pages are the ones that
  1674. // really will never be referenced again.
  1675. //
  1676. // Note this isn't done for session images as they're inpaged directly
  1677. // from the filesystem via the section.
  1678. //
  1679. if (LoadInSessionSpace == 0) {
  1680. MmPurgeSection (ControlArea->FilePointer->SectionObjectPointer,
  1681. NULL,
  1682. 0,
  1683. FALSE);
  1684. }
  1685. if (OpaqueSession != NULL) {
  1686. MiDetachFromSecureProcessInSession (OpaqueSession, &ApcState);
  1687. }
  1688. //
  1689. // Indicate that this section has been loaded into the system.
  1690. //
  1691. SectionPointer->Segment->SystemImageBase = *ImageBaseAddress;
  1692. if (LoadInSessionSpace == 0) {
  1693. //
  1694. // Return any excess resident available and commit.
  1695. //
  1696. if (PagesRequired != ActualPagesUsed) {
  1697. ASSERT (PagesRequired > ActualPagesUsed);
  1698. PagesRequired -= ActualPagesUsed;
  1699. MiReturnResidentAvailable (PagesRequired, 13);
  1700. MiReturnCommitment (PagesRequired);
  1701. }
  1702. }
  1703. return Status;
  1704. }
  1705. VOID
  1706. MmFreeDriverInitialization (
  1707. IN PVOID ImageHandle
  1708. )
  1709. /*++
  1710. Routine Description:
  1711. This routine removes the pages that relocate and debug information from
  1712. the address space of the driver.
  1713. NOTE: This routine looks at the last sections defined in the image
  1714. header and if that section is marked as DISCARDABLE in the
  1715. characteristics, it is removed from the image. This means
  1716. that all discardable sections at the end of the driver are
  1717. deleted.
  1718. Arguments:
  1719. SectionObject - Supplies the section object for the image.
  1720. Return Value:
  1721. None.
  1722. --*/
  1723. {
  1724. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  1725. PMMPTE PointerPte;
  1726. PMMPTE LastPte;
  1727. PFN_NUMBER NumberOfPtes;
  1728. PVOID Base;
  1729. ULONG i;
  1730. PIMAGE_NT_HEADERS NtHeaders;
  1731. PIMAGE_SECTION_HEADER NtSection;
  1732. PIMAGE_SECTION_HEADER FoundSection;
  1733. PFN_NUMBER PagesDeleted;
  1734. DataTableEntry = (PKLDR_DATA_TABLE_ENTRY)ImageHandle;
  1735. Base = DataTableEntry->DllBase;
  1736. ASSERT (MI_IS_SESSION_ADDRESS (Base) == FALSE);
  1737. NumberOfPtes = DataTableEntry->SizeOfImage >> PAGE_SHIFT;
  1738. LastPte = MiGetPteAddress (Base) + NumberOfPtes;
  1739. NtHeaders = (PIMAGE_NT_HEADERS)RtlImageNtHeader(Base);
  1740. NtSection = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeaders +
  1741. sizeof(ULONG) +
  1742. sizeof(IMAGE_FILE_HEADER) +
  1743. NtHeaders->FileHeader.SizeOfOptionalHeader
  1744. );
  1745. NtSection += NtHeaders->FileHeader.NumberOfSections;
  1746. FoundSection = NULL;
  1747. for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i += 1) {
  1748. NtSection -= 1;
  1749. if ((NtSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) {
  1750. FoundSection = NtSection;
  1751. }
  1752. else {
  1753. //
  1754. // There was a non discardable section between the this
  1755. // section and the last non discardable section, don't
  1756. // discard this section and don't look any more.
  1757. //
  1758. break;
  1759. }
  1760. }
  1761. if (FoundSection != NULL) {
  1762. PointerPte = MiGetPteAddress ((PVOID)(ROUND_TO_PAGES (
  1763. (PCHAR)Base + FoundSection->VirtualAddress)));
  1764. NumberOfPtes = (PFN_NUMBER)(LastPte - PointerPte);
  1765. PagesDeleted = MiDeleteSystemPagableVm (PointerPte,
  1766. NumberOfPtes,
  1767. ZeroKernelPte,
  1768. FALSE,
  1769. NULL);
  1770. MiReturnResidentAvailable (PagesDeleted, 18);
  1771. MiReturnCommitment (PagesDeleted);
  1772. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_DRIVER_INIT_CODE, PagesDeleted);
  1773. InterlockedExchangeAdd ((PLONG)&MmDriverCommit,
  1774. (LONG) (0 - PagesDeleted));
  1775. }
  1776. return;
  1777. }
  1778. LOGICAL
  1779. MiChargeResidentAvailable (
  1780. IN PFN_NUMBER NumberOfPages,
  1781. IN ULONG Id
  1782. )
  1783. /*++
  1784. Routine Description:
  1785. This routine is a nonpaged wrapper to charge resident available pages.
  1786. Arguments:
  1787. NumberOfPages - Supplies the number of pages to charge.
  1788. Id - Supplies a tracking ID for debugging purposes.
  1789. Return Value:
  1790. TRUE if the pages were charged, FALSE if not.
  1791. --*/
  1792. {
  1793. KIRQL OldIrql;
  1794. LOCK_PFN (OldIrql);
  1795. if (MI_NONPAGABLE_MEMORY_AVAILABLE() <= (SPFN_NUMBER)NumberOfPages) {
  1796. UNLOCK_PFN (OldIrql);
  1797. return FALSE;
  1798. }
  1799. MmResidentAvailablePages -= NumberOfPages;
  1800. MM_BUMP_COUNTER(Id, NumberOfPages);
  1801. UNLOCK_PFN (OldIrql);
  1802. return TRUE;
  1803. }
  1804. VOID
  1805. MiReturnResidentAvailable (
  1806. IN PFN_NUMBER NumberOfPages,
  1807. IN ULONG Id
  1808. )
  1809. /*++
  1810. Routine Description:
  1811. This routine is a nonpaged wrapper to return resident available pages.
  1812. Arguments:
  1813. NumberOfPages - Supplies the number of pages to return.
  1814. Id - Supplies a tracking ID for debugging purposes.
  1815. Return Value:
  1816. None.
  1817. --*/
  1818. {
  1819. KIRQL OldIrql;
  1820. LOCK_PFN (OldIrql);
  1821. MmResidentAvailablePages += NumberOfPages;
  1822. MM_BUMP_COUNTER(Id, NumberOfPages);
  1823. UNLOCK_PFN (OldIrql);
  1824. }
  1825. VOID
  1826. MiEnablePagingOfDriver (
  1827. IN PVOID ImageHandle
  1828. )
  1829. {
  1830. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  1831. PMMPTE LastPte;
  1832. PMMPTE PointerPte;
  1833. PVOID Base;
  1834. ULONG i;
  1835. PIMAGE_NT_HEADERS NtHeaders;
  1836. PIMAGE_SECTION_HEADER FoundSection;
  1837. PIMAGE_OPTIONAL_HEADER OptionalHeader;
  1838. //
  1839. // Don't page kernel mode code if customer does not want it paged.
  1840. //
  1841. if (MmDisablePagingExecutive & MM_SYSTEM_CODE_LOCKED_DOWN) {
  1842. return;
  1843. }
  1844. //
  1845. // If the driver has pagable code, make it paged.
  1846. //
  1847. DataTableEntry = (PKLDR_DATA_TABLE_ENTRY)ImageHandle;
  1848. Base = DataTableEntry->DllBase;
  1849. NtHeaders = (PIMAGE_NT_HEADERS)RtlImageNtHeader(Base);
  1850. OptionalHeader = (PIMAGE_OPTIONAL_HEADER)((PCHAR)NtHeaders +
  1851. #if defined (_WIN64)
  1852. FIELD_OFFSET (IMAGE_NT_HEADERS64, OptionalHeader));
  1853. #else
  1854. FIELD_OFFSET (IMAGE_NT_HEADERS32, OptionalHeader));
  1855. #endif
  1856. FoundSection = IMAGE_FIRST_SECTION (NtHeaders);
  1857. i = NtHeaders->FileHeader.NumberOfSections;
  1858. PointerPte = NULL;
  1859. //
  1860. // Initializing LastPte is not needed for correctness, but without it
  1861. // the compiler cannot compile this code W4 to check for use of
  1862. // uninitialized variables.
  1863. //
  1864. LastPte = NULL;
  1865. while (i > 0) {
  1866. #if DBG
  1867. if ((*(PULONG)FoundSection->Name == 'tini') ||
  1868. (*(PULONG)FoundSection->Name == 'egap')) {
  1869. DbgPrint("driver %wZ has lower case sections (init or pagexxx)\n",
  1870. &DataTableEntry->FullDllName);
  1871. }
  1872. #endif //DBG
  1873. //
  1874. // Mark as pagable any section which starts with the
  1875. // first 4 characters PAGE or .eda (for the .edata section).
  1876. //
  1877. if ((*(PULONG)FoundSection->Name == 'EGAP') ||
  1878. (*(PULONG)FoundSection->Name == 'ade.')) {
  1879. //
  1880. // This section is pagable, save away the start and end.
  1881. //
  1882. if (PointerPte == NULL) {
  1883. //
  1884. // Previous section was NOT pagable, get the start address.
  1885. //
  1886. PointerPte = MiGetPteAddress ((PVOID)(ROUND_TO_PAGES (
  1887. (PCHAR)Base + FoundSection->VirtualAddress)));
  1888. }
  1889. LastPte = MiGetPteAddress ((PCHAR)Base +
  1890. FoundSection->VirtualAddress +
  1891. (OptionalHeader->SectionAlignment - 1) +
  1892. FoundSection->SizeOfRawData - PAGE_SIZE);
  1893. }
  1894. else {
  1895. //
  1896. // This section is not pagable, if the previous section was
  1897. // pagable, enable it.
  1898. //
  1899. if (PointerPte != NULL) {
  1900. MiSetPagingOfDriver (PointerPte, LastPte, FALSE);
  1901. PointerPte = NULL;
  1902. }
  1903. }
  1904. i -= 1;
  1905. FoundSection += 1;
  1906. }
  1907. if (PointerPte != NULL) {
  1908. MiSetPagingOfDriver (PointerPte, LastPte, FALSE);
  1909. }
  1910. }
  1911. VOID
  1912. MiSetPagingOfDriver (
  1913. IN PMMPTE PointerPte,
  1914. IN PMMPTE LastPte,
  1915. IN LOGICAL SessionSpace
  1916. )
  1917. /*++
  1918. Routine Description:
  1919. This routine marks the specified range of PTEs as pagable.
  1920. Arguments:
  1921. PointerPte - Supplies the starting PTE.
  1922. LastPte - Supplies the ending PTE.
  1923. SessionSpace - Supplies TRUE if this mapping is in session space,
  1924. FALSE if not.
  1925. Return Value:
  1926. None.
  1927. Environment:
  1928. Kernel Mode, IRQL of APC_LEVEL or below.
  1929. This routine could be made PAGELK but it is a high frequency routine
  1930. so it is actually better to keep it nonpaged to avoid bringing in the
  1931. entire PAGELK section.
  1932. --*/
  1933. {
  1934. PVOID Base;
  1935. PFN_NUMBER PageCount;
  1936. PFN_NUMBER PageFrameIndex;
  1937. PMMPFN Pfn;
  1938. MMPTE TempPte;
  1939. MMPTE PreviousPte;
  1940. KIRQL OldIrql1;
  1941. KIRQL OldIrql;
  1942. PAGED_CODE ();
  1943. ASSERT ((SessionSpace == FALSE) ||
  1944. (MmIsAddressValid(MmSessionSpace) == TRUE));
  1945. if (MI_IS_PHYSICAL_ADDRESS(MiGetVirtualAddressMappedByPte(PointerPte))) {
  1946. //
  1947. // No need to lock physical addresses.
  1948. //
  1949. return;
  1950. }
  1951. PageCount = 0;
  1952. if (SessionSpace == TRUE) {
  1953. LOCK_SESSION_SPACE_WS (OldIrql1, PsGetCurrentThread ());
  1954. }
  1955. else {
  1956. LOCK_SYSTEM_WS (OldIrql1, PsGetCurrentThread ());
  1957. }
  1958. LOCK_PFN (OldIrql);
  1959. Base = MiGetVirtualAddressMappedByPte (PointerPte);
  1960. while (PointerPte <= LastPte) {
  1961. //
  1962. // Check to make sure this PTE has not already been
  1963. // made pagable (or deleted). It is pagable if it
  1964. // is not valid, or if the PFN database wsindex element
  1965. // is non zero.
  1966. //
  1967. if (PointerPte->u.Hard.Valid == 1) {
  1968. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  1969. Pfn = MI_PFN_ELEMENT (PageFrameIndex);
  1970. ASSERT (Pfn->u2.ShareCount == 1);
  1971. if (Pfn->u1.WsIndex == 0) {
  1972. //
  1973. // Original PTE may need to be set for drivers loaded
  1974. // via ntldr.
  1975. //
  1976. if (Pfn->OriginalPte.u.Long == 0) {
  1977. Pfn->OriginalPte.u.Long = MM_KERNEL_DEMAND_ZERO_PTE;
  1978. Pfn->OriginalPte.u.Soft.Protection |= MM_EXECUTE;
  1979. }
  1980. TempPte = *PointerPte;
  1981. MI_MAKE_VALID_PTE_TRANSITION (TempPte,
  1982. Pfn->OriginalPte.u.Soft.Protection);
  1983. PreviousPte.u.Flush = KeFlushSingleTb (Base,
  1984. TRUE,
  1985. TRUE,
  1986. (PHARDWARE_PTE)PointerPte,
  1987. TempPte.u.Flush);
  1988. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PreviousPte, Pfn);
  1989. //
  1990. // Flush the translation buffer and decrement the number of valid
  1991. // PTEs within the containing page table page. Note that for a
  1992. // private page, the page table page is still needed because the
  1993. // page is in transition.
  1994. //
  1995. MiDecrementShareCount (PageFrameIndex);
  1996. MmResidentAvailablePages += 1;
  1997. MM_BUMP_COUNTER(19, 1);
  1998. MmTotalSystemDriverPages += 1;
  1999. PageCount += 1;
  2000. }
  2001. else {
  2002. //
  2003. // This page is already pagable and has a WSLE entry.
  2004. // Ignore it here and let the trimmer take it if memory
  2005. // comes under pressure.
  2006. //
  2007. }
  2008. }
  2009. Base = (PVOID)((PCHAR)Base + PAGE_SIZE);
  2010. PointerPte += 1;
  2011. }
  2012. if (SessionSpace == TRUE) {
  2013. //
  2014. // Session space has no ASN - flush the entire TB.
  2015. //
  2016. MI_FLUSH_ENTIRE_SESSION_TB (TRUE, TRUE);
  2017. }
  2018. UNLOCK_PFN (OldIrql);
  2019. if (SessionSpace == TRUE) {
  2020. //
  2021. // These pages are no longer locked down.
  2022. //
  2023. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_PAGE_DRIVER, (ULONG)PageCount);
  2024. MmSessionSpace->NonPagablePages -= PageCount;
  2025. MM_BUMP_SESS_COUNTER(MM_DBG_SESSION_DRIVER_PAGES_UNLOCKED, (ULONG)PageCount);
  2026. UNLOCK_SESSION_SPACE_WS (OldIrql1);
  2027. }
  2028. else {
  2029. UNLOCK_SYSTEM_WS (OldIrql1);
  2030. }
  2031. }
  2032. PVOID
  2033. MmPageEntireDriver (
  2034. IN PVOID AddressWithinSection
  2035. )
  2036. /*++
  2037. Routine Description:
  2038. This routine allows a driver to page out all of its code and
  2039. data regardless of the attributes of the various image sections.
  2040. Note, this routine can be called multiple times with no
  2041. intervening calls to MmResetDriverPaging.
  2042. Arguments:
  2043. AddressWithinSection - Supplies an address within the driver, e.g.
  2044. DriverEntry.
  2045. Return Value:
  2046. Base address of driver.
  2047. Environment:
  2048. Kernel mode, APC_LEVEL or below.
  2049. --*/
  2050. {
  2051. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  2052. PMMPTE FirstPte;
  2053. PMMPTE LastPte;
  2054. PVOID BaseAddress;
  2055. PSECTION SectionPointer;
  2056. LOGICAL SessionSpace;
  2057. PAGED_CODE();
  2058. DataTableEntry = MiLookupDataTableEntry (AddressWithinSection, FALSE);
  2059. if (DataTableEntry == NULL) {
  2060. return NULL;
  2061. }
  2062. //
  2063. // Don't page kernel mode code if disabled via registry.
  2064. //
  2065. if (MmDisablePagingExecutive & MM_SYSTEM_CODE_LOCKED_DOWN) {
  2066. BaseAddress = DataTableEntry->DllBase;
  2067. goto success;
  2068. }
  2069. SectionPointer = (PSECTION)DataTableEntry->SectionPointer;
  2070. SessionSpace = MI_IS_SESSION_IMAGE_ADDRESS (AddressWithinSection);
  2071. if ((SectionPointer != NULL) && (SectionPointer != (PVOID)-1)) {
  2072. //
  2073. // Driver is mapped as an image (ie: win32k), this is always pagable.
  2074. // For session space, an image that has been loaded at its desired
  2075. // address is also always pagable. If there was an address collision,
  2076. // then we fall through because we have to explicitly page it.
  2077. //
  2078. if (SessionSpace == TRUE) {
  2079. if (SectionPointer->Segment &&
  2080. SectionPointer->Segment->BasedAddress == SectionPointer->Segment->SystemImageBase) {
  2081. BaseAddress = DataTableEntry->DllBase;
  2082. goto success;
  2083. }
  2084. }
  2085. else {
  2086. BaseAddress = DataTableEntry->DllBase;
  2087. goto success;
  2088. }
  2089. }
  2090. BaseAddress = DataTableEntry->DllBase;
  2091. FirstPte = MiGetPteAddress (BaseAddress);
  2092. LastPte = (FirstPte - 1) + (DataTableEntry->SizeOfImage >> PAGE_SHIFT);
  2093. MiSetPagingOfDriver (FirstPte, LastPte, SessionSpace);
  2094. success:
  2095. #if 0
  2096. //
  2097. // .rdata and .pdata must be made resident for the kernel debugger to
  2098. // display stack frames properly. This means these sections must not
  2099. // only be marked nonpagable, but for drivers like win32k.sys (and
  2100. // session space drivers that are not relocated) that are paged in and
  2101. // out directly from the filesystem, these pages must be made explicitly
  2102. // resident now.
  2103. //
  2104. // If the debugger can be pitched, then there is no need to lock down
  2105. // these subsections as no one will be debugging this system anyway.
  2106. //
  2107. if (KdPitchDebugger == 0) {
  2108. MiLockDriverPdata (DataTableEntry);
  2109. }
  2110. #endif
  2111. return BaseAddress;
  2112. }
  2113. VOID
  2114. MmResetDriverPaging (
  2115. IN PVOID AddressWithinSection
  2116. )
  2117. /*++
  2118. Routine Description:
  2119. This routines resets the driver paging to what the image specified.
  2120. Hence image sections such as the IAT, .text, .data will be locked
  2121. down in memory.
  2122. Note, there is no requirement that MmPageEntireDriver was called.
  2123. Arguments:
  2124. AddressWithinSection - Supplies an address within the driver, e.g.
  2125. DriverEntry.
  2126. Return Value:
  2127. None.
  2128. Environment:
  2129. Kernel mode, APC_LEVEL or below.
  2130. --*/
  2131. {
  2132. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  2133. PMMPTE LastPte;
  2134. PMMPTE PointerPte;
  2135. PVOID Base;
  2136. ULONG i;
  2137. PIMAGE_NT_HEADERS NtHeaders;
  2138. PIMAGE_SECTION_HEADER FoundSection;
  2139. PAGED_CODE();
  2140. //
  2141. // Don't page kernel mode code if disabled via registry.
  2142. //
  2143. if (MmDisablePagingExecutive & MM_SYSTEM_CODE_LOCKED_DOWN) {
  2144. return;
  2145. }
  2146. if (MI_IS_PHYSICAL_ADDRESS(AddressWithinSection)) {
  2147. return;
  2148. }
  2149. //
  2150. // If the driver has pagable code, make it paged.
  2151. //
  2152. DataTableEntry = MiLookupDataTableEntry (AddressWithinSection, FALSE);
  2153. if ((DataTableEntry->SectionPointer != NULL) &&
  2154. (DataTableEntry->SectionPointer != (PVOID)-1)) {
  2155. //
  2156. // Driver is mapped by image hence already paged.
  2157. //
  2158. return;
  2159. }
  2160. Base = DataTableEntry->DllBase;
  2161. NtHeaders = (PIMAGE_NT_HEADERS)RtlImageNtHeader(Base);
  2162. FoundSection = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeaders +
  2163. sizeof(ULONG) +
  2164. sizeof(IMAGE_FILE_HEADER) +
  2165. NtHeaders->FileHeader.SizeOfOptionalHeader
  2166. );
  2167. i = NtHeaders->FileHeader.NumberOfSections;
  2168. PointerPte = NULL;
  2169. while (i > 0) {
  2170. #if DBG
  2171. if ((*(PULONG)FoundSection->Name == 'tini') ||
  2172. (*(PULONG)FoundSection->Name == 'egap')) {
  2173. DbgPrint("driver %wZ has lower case sections (init or pagexxx)\n",
  2174. &DataTableEntry->FullDllName);
  2175. }
  2176. #endif
  2177. //
  2178. // Don't lock down code for sections marked as discardable or
  2179. // sections marked with the first 4 characters PAGE or .eda
  2180. // (for the .edata section) or INIT.
  2181. //
  2182. if (((FoundSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) ||
  2183. (*(PULONG)FoundSection->Name == 'EGAP') ||
  2184. (*(PULONG)FoundSection->Name == 'ade.') ||
  2185. (*(PULONG)FoundSection->Name == 'TINI')) {
  2186. NOTHING;
  2187. }
  2188. else {
  2189. //
  2190. // This section is nonpagable.
  2191. //
  2192. PointerPte = MiGetPteAddress (
  2193. (PCHAR)Base + FoundSection->VirtualAddress);
  2194. LastPte = MiGetPteAddress ((PCHAR)Base +
  2195. FoundSection->VirtualAddress +
  2196. (FoundSection->SizeOfRawData - 1));
  2197. ASSERT (PointerPte <= LastPte);
  2198. MiLockCode (PointerPte, LastPte, MM_LOCK_BY_NONPAGE);
  2199. }
  2200. i -= 1;
  2201. FoundSection += 1;
  2202. }
  2203. return;
  2204. }
  2205. #if 0
  2206. VOID
  2207. MiLockDriverPdata (
  2208. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  2209. )
  2210. /*++
  2211. Routine Description:
  2212. This routines locks down the .pdata & .rdata subsections within the driver.
  2213. Arguments:
  2214. DataTableEntry - Supplies the argument module's data table entry.
  2215. Return Value:
  2216. None.
  2217. Environment:
  2218. Kernel mode, APC_LEVEL or below.
  2219. --*/
  2220. {
  2221. PSECTION SectionPointer;
  2222. LOGICAL SessionSpace;
  2223. PMMPTE LastPte;
  2224. PMMPTE PointerPte;
  2225. PVOID Base;
  2226. ULONG i;
  2227. PIMAGE_NT_HEADERS NtHeaders;
  2228. PIMAGE_SECTION_HEADER FoundSection;
  2229. PETHREAD Thread;
  2230. PAGED_CODE();
  2231. //
  2232. // If the debugger can be pitched, then there is no need to lock down
  2233. // these subsections as no one will be debugging this system anyway.
  2234. //
  2235. ASSERT (KdPitchDebugger == 0);
  2236. if (MI_IS_PHYSICAL_ADDRESS(DataTableEntry->DllBase)) {
  2237. return;
  2238. }
  2239. SectionPointer = (PSECTION)DataTableEntry->SectionPointer;
  2240. //
  2241. // Drivers backed by the paging file require no handling as the .pdata
  2242. // .rdata sections are not paged by default and have already been inpaged.
  2243. //
  2244. if ((SectionPointer == NULL) || (SectionPointer == (PVOID)-1)) {
  2245. return;
  2246. }
  2247. //
  2248. // The driver is mapped as an image (ie: win32k) which is always pagable.
  2249. // In fact, it may not even be resident right now because in addition
  2250. // to being always pagable, it is backed directly by the filesystem,
  2251. // not the pagefile. So it must be inpaged now.
  2252. //
  2253. SessionSpace = MI_IS_SESSION_IMAGE_ADDRESS (DataTableEntry->DllBase);
  2254. Base = DataTableEntry->DllBase;
  2255. NtHeaders = (PIMAGE_NT_HEADERS)RtlImageNtHeader(Base);
  2256. FoundSection = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeaders +
  2257. sizeof(ULONG) +
  2258. sizeof(IMAGE_FILE_HEADER) +
  2259. NtHeaders->FileHeader.SizeOfOptionalHeader);
  2260. i = NtHeaders->FileHeader.NumberOfSections;
  2261. PointerPte = NULL;
  2262. Thread = PsGetCurrentThread ();
  2263. for ( ; i > 0; i -= 1, FoundSection += 1) {
  2264. //
  2265. // Don't lock down discardable sections.
  2266. //
  2267. if ((FoundSection->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) != 0) {
  2268. NOTHING;
  2269. }
  2270. //
  2271. // Inpage and lock down subsections marked as .pdata & .rdata.
  2272. //
  2273. if (((*(PULONG)FoundSection->Name == 'adp.') &&
  2274. (FoundSection->Name[4] == 't') &&
  2275. (FoundSection->Name[5] == 'a')) ||
  2276. ((*(PULONG)FoundSection->Name == 'adr.') &&
  2277. (FoundSection->Name[4] == 't') &&
  2278. (FoundSection->Name[5] == 'a'))) {
  2279. PointerPte = MiGetPteAddress (
  2280. (PCHAR)Base + FoundSection->VirtualAddress);
  2281. LastPte = MiGetPteAddress ((PCHAR)Base +
  2282. FoundSection->VirtualAddress +
  2283. (FoundSection->SizeOfRawData - 1));
  2284. ASSERT (PointerPte <= LastPte);
  2285. }
  2286. }
  2287. return;
  2288. }
  2289. #endif
  2290. VOID
  2291. MiClearImports(
  2292. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  2293. )
  2294. /*++
  2295. Routine Description:
  2296. Free up the import list and clear the pointer. This stops the
  2297. recursion performed in MiDereferenceImports().
  2298. Arguments:
  2299. DataTableEntry - provided for the driver.
  2300. Return Value:
  2301. Status of the import list construction operation.
  2302. --*/
  2303. {
  2304. PAGED_CODE();
  2305. if (DataTableEntry->LoadedImports == (PVOID)LOADED_AT_BOOT) {
  2306. return;
  2307. }
  2308. if (DataTableEntry->LoadedImports == (PVOID)NO_IMPORTS_USED) {
  2309. NOTHING;
  2310. }
  2311. else if (SINGLE_ENTRY(DataTableEntry->LoadedImports)) {
  2312. NOTHING;
  2313. }
  2314. else {
  2315. //
  2316. // free the memory
  2317. //
  2318. ExFreePool ((PVOID)DataTableEntry->LoadedImports);
  2319. }
  2320. //
  2321. // stop the recursion
  2322. //
  2323. DataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  2324. }
  2325. VOID
  2326. MiRememberUnloadedDriver (
  2327. IN PUNICODE_STRING DriverName,
  2328. IN PVOID Address,
  2329. IN ULONG Length
  2330. )
  2331. /*++
  2332. Routine Description:
  2333. This routine saves information about unloaded drivers so that ones that
  2334. forget to delete lookaside lists or queues can be caught.
  2335. Arguments:
  2336. DriverName - Supplies a Unicode string containing the driver's name.
  2337. Address - Supplies the address the driver was loaded at.
  2338. Length - Supplies the number of bytes the driver load spanned.
  2339. Return Value:
  2340. None.
  2341. --*/
  2342. {
  2343. PUNLOADED_DRIVERS Entry;
  2344. ULONG NumberOfBytes;
  2345. if (DriverName->Length == 0) {
  2346. //
  2347. // This is an aborted load and the driver name hasn't been filled
  2348. // in yet. No need to save it.
  2349. //
  2350. return;
  2351. }
  2352. //
  2353. // Serialization is provided by the caller, so just update the list now.
  2354. // Note the allocations are nonpaged so they can be searched at bugcheck
  2355. // time.
  2356. //
  2357. if (MmUnloadedDrivers == NULL) {
  2358. NumberOfBytes = MI_UNLOADED_DRIVERS * sizeof (UNLOADED_DRIVERS);
  2359. MmUnloadedDrivers = (PUNLOADED_DRIVERS)ExAllocatePoolWithTag (NonPagedPool,
  2360. NumberOfBytes,
  2361. 'TDmM');
  2362. if (MmUnloadedDrivers == NULL) {
  2363. return;
  2364. }
  2365. RtlZeroMemory (MmUnloadedDrivers, NumberOfBytes);
  2366. MmLastUnloadedDriver = 0;
  2367. }
  2368. else if (MmLastUnloadedDriver >= MI_UNLOADED_DRIVERS) {
  2369. MmLastUnloadedDriver = 0;
  2370. }
  2371. Entry = &MmUnloadedDrivers[MmLastUnloadedDriver];
  2372. //
  2373. // Free the old entry as we recycle into the new.
  2374. //
  2375. RtlFreeUnicodeString (&Entry->Name);
  2376. Entry->Name.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  2377. DriverName->Length,
  2378. 'TDmM');
  2379. if (Entry->Name.Buffer == NULL) {
  2380. Entry->Name.MaximumLength = 0;
  2381. Entry->Name.Length = 0;
  2382. MiUnloadsSkipped += 1;
  2383. return;
  2384. }
  2385. RtlCopyMemory(Entry->Name.Buffer, DriverName->Buffer, DriverName->Length);
  2386. Entry->Name.Length = DriverName->Length;
  2387. Entry->Name.MaximumLength = DriverName->MaximumLength;
  2388. Entry->StartAddress = Address;
  2389. Entry->EndAddress = (PVOID)((PCHAR)Address + Length);
  2390. KeQuerySystemTime (&Entry->CurrentTime);
  2391. MiTotalUnloads += 1;
  2392. MmLastUnloadedDriver += 1;
  2393. }
  2394. PUNICODE_STRING
  2395. MmLocateUnloadedDriver (
  2396. IN PVOID VirtualAddress
  2397. )
  2398. /*++
  2399. Routine Description:
  2400. This routine attempts to find the specified virtual address in the
  2401. unloaded driver list.
  2402. Arguments:
  2403. VirtualAddress - Supplies a virtual address that might be within a driver
  2404. that has already unloaded.
  2405. Return Value:
  2406. A pointer to a Unicode string containing the unloaded driver's name.
  2407. Environment:
  2408. Kernel mode, bugcheck time.
  2409. --*/
  2410. {
  2411. PUNLOADED_DRIVERS Entry;
  2412. ULONG i;
  2413. ULONG Index;
  2414. //
  2415. // No serialization is needed because we've crashed.
  2416. //
  2417. if (MmUnloadedDrivers == NULL) {
  2418. return NULL;
  2419. }
  2420. Index = MmLastUnloadedDriver - 1;
  2421. for (i = 0; i < MI_UNLOADED_DRIVERS; i += 1) {
  2422. if (Index >= MI_UNLOADED_DRIVERS) {
  2423. Index = MI_UNLOADED_DRIVERS - 1;
  2424. }
  2425. Entry = &MmUnloadedDrivers[Index];
  2426. if (Entry->Name.Buffer != NULL) {
  2427. if ((VirtualAddress >= Entry->StartAddress) &&
  2428. (VirtualAddress < Entry->EndAddress)) {
  2429. return &Entry->Name;
  2430. }
  2431. }
  2432. Index -= 1;
  2433. }
  2434. return NULL;
  2435. }
  2436. NTSTATUS
  2437. MmUnloadSystemImage (
  2438. IN PVOID ImageHandle
  2439. )
  2440. /*++
  2441. Routine Description:
  2442. This routine unloads a previously loaded system image and returns
  2443. the allocated resources.
  2444. Arguments:
  2445. ImageHandle - Supplies a pointer to the section object of the
  2446. image to unload.
  2447. Return Value:
  2448. Various NTSTATUS codes.
  2449. Environment:
  2450. Kernel mode, APC_LEVEL or below, arbitrary process context.
  2451. --*/
  2452. {
  2453. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  2454. PMMPTE LastPte;
  2455. PFN_NUMBER PagesRequired;
  2456. PFN_NUMBER ResidentPages;
  2457. PMMPTE PointerPte;
  2458. PFN_NUMBER NumberOfPtes;
  2459. PVOID BasedAddress;
  2460. SIZE_T NumberOfBytes;
  2461. LOGICAL MustFree;
  2462. SIZE_T CommittedPages;
  2463. LOGICAL ViewDeleted;
  2464. PIMAGE_ENTRY_IN_SESSION DriverImage;
  2465. NTSTATUS Status;
  2466. PSECTION SectionPointer;
  2467. PKTHREAD CurrentThread;
  2468. //
  2469. // Arbitrary process context so prevent suspend APCs now.
  2470. //
  2471. CurrentThread = KeGetCurrentThread ();
  2472. KeEnterCriticalRegionThread (CurrentThread);
  2473. KeWaitForSingleObject (&MmSystemLoadLock,
  2474. WrVirtualMemory,
  2475. KernelMode,
  2476. FALSE,
  2477. (PLARGE_INTEGER)NULL);
  2478. ViewDeleted = FALSE;
  2479. DataTableEntry = (PKLDR_DATA_TABLE_ENTRY)ImageHandle;
  2480. BasedAddress = DataTableEntry->DllBase;
  2481. #if DBGXX
  2482. //
  2483. // MiUnloadSystemImageByForce violates this check so remove it for now.
  2484. //
  2485. if (PsLoadedModuleList.Flink) {
  2486. LOGICAL Found;
  2487. PLIST_ENTRY NextEntry;
  2488. PKLDR_DATA_TABLE_ENTRY DataTableEntry2;
  2489. Found = FALSE;
  2490. NextEntry = PsLoadedModuleList.Flink;
  2491. while (NextEntry != &PsLoadedModuleList) {
  2492. DataTableEntry2 = CONTAINING_RECORD(NextEntry,
  2493. KLDR_DATA_TABLE_ENTRY,
  2494. InLoadOrderLinks);
  2495. if (DataTableEntry == DataTableEntry2) {
  2496. Found = TRUE;
  2497. break;
  2498. }
  2499. NextEntry = NextEntry->Flink;
  2500. }
  2501. ASSERT (Found == TRUE);
  2502. }
  2503. #endif
  2504. #if DBG_SYSLOAD
  2505. if (DataTableEntry->SectionPointer == NULL) {
  2506. DbgPrint ("MM: Called to unload boot driver %wZ\n",
  2507. &DataTableEntry->FullDllName);
  2508. }
  2509. else {
  2510. DbgPrint ("MM: Called to unload non-boot driver %wZ\n",
  2511. &DataTableEntry->FullDllName);
  2512. }
  2513. #endif
  2514. //
  2515. // Any driver loaded at boot that did not have its import list
  2516. // and LoadCount reconstructed cannot be unloaded because we don't
  2517. // know how many other drivers may be linked to it.
  2518. //
  2519. if (DataTableEntry->LoadedImports == (PVOID)LOADED_AT_BOOT) {
  2520. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  2521. KeLeaveCriticalRegionThread (CurrentThread);
  2522. return STATUS_SUCCESS;
  2523. }
  2524. ASSERT (DataTableEntry->LoadCount != 0);
  2525. if (MI_IS_SESSION_IMAGE_ADDRESS (BasedAddress)) {
  2526. //
  2527. // A printer driver may be referenced multiple times for the
  2528. // same session space. Only unload the last reference.
  2529. //
  2530. DriverImage = MiSessionLookupImage (BasedAddress);
  2531. ASSERT (DriverImage);
  2532. ASSERT (DriverImage->ImageCountInThisSession);
  2533. if (DriverImage->ImageCountInThisSession > 1) {
  2534. DriverImage->ImageCountInThisSession -= 1;
  2535. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  2536. KeLeaveCriticalRegionThread (CurrentThread);
  2537. return STATUS_SUCCESS;
  2538. }
  2539. //
  2540. // The reference count for this image has dropped to zero in this
  2541. // session, so we can delete this session's view of the image.
  2542. //
  2543. Status = MiSessionWideGetImageSize (BasedAddress,
  2544. &NumberOfBytes,
  2545. &CommittedPages);
  2546. if (!NT_SUCCESS(Status)) {
  2547. KeBugCheckEx (MEMORY_MANAGEMENT,
  2548. 0x41286,
  2549. (ULONG_PTR)MmSessionSpace->SessionId,
  2550. (ULONG_PTR)BasedAddress,
  2551. 0);
  2552. }
  2553. //
  2554. // Free the session space taken up by the image, unmapping it from
  2555. // the current VA space - note this does not remove page table pages
  2556. // from the session PageTables[]. Each data page is only freed
  2557. // if there are no other references to it (ie: from any other
  2558. // sessions).
  2559. //
  2560. PointerPte = MiGetPteAddress (BasedAddress);
  2561. LastPte = MiGetPteAddress ((PVOID)((ULONG_PTR)BasedAddress + NumberOfBytes));
  2562. PagesRequired = MiDeleteSystemPagableVm (PointerPte,
  2563. (PFN_NUMBER)(LastPte - PointerPte),
  2564. ZeroKernelPte,
  2565. TRUE,
  2566. &ResidentPages);
  2567. //
  2568. // Note resident available is returned here without waiting for load
  2569. // count to reach zero because it is charged each time a session space
  2570. // driver locks down its code or data regardless of whether it is really
  2571. // the same copy-on-write backing page(s) that some other session has
  2572. // already locked down.
  2573. //
  2574. MiReturnResidentAvailable (ResidentPages, 22);
  2575. if ((MmDisablePagingExecutive & MM_SYSTEM_CODE_LOCKED_DOWN) == 0) {
  2576. SectionPointer = (PSECTION)DataTableEntry->SectionPointer;
  2577. if ((SectionPointer == NULL) ||
  2578. (SectionPointer == (PVOID)-1) ||
  2579. (SectionPointer->Segment == NULL) ||
  2580. (SectionPointer->Segment->BasedAddress != SectionPointer->Segment->SystemImageBase)) {
  2581. MmTotalSystemDriverPages -= (ULONG)(PagesRequired - ResidentPages);
  2582. }
  2583. }
  2584. InterlockedExchangeAddSizeT (&MmSessionSpace->CommittedPages,
  2585. 0 - CommittedPages);
  2586. MM_BUMP_SESS_COUNTER(MM_DBG_SESSION_COMMIT_IMAGE_UNLOAD,
  2587. (ULONG)CommittedPages);
  2588. ViewDeleted = TRUE;
  2589. //
  2590. // Return the commitment we took out on the pagefile when the
  2591. // image was allocated.
  2592. //
  2593. MiReturnCommitment (CommittedPages);
  2594. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_DRIVER_UNLOAD, CommittedPages);
  2595. //
  2596. // Tell the session space image handler that we are releasing
  2597. // our claim to the image.
  2598. //
  2599. Status = MiRemoveImageSessionWide (BasedAddress);
  2600. ASSERT (NT_SUCCESS (Status));
  2601. }
  2602. ASSERT (DataTableEntry->LoadCount != 0);
  2603. DataTableEntry->LoadCount -= 1;
  2604. if (DataTableEntry->LoadCount != 0) {
  2605. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  2606. KeLeaveCriticalRegionThread (CurrentThread);
  2607. return STATUS_SUCCESS;
  2608. }
  2609. #if DBG
  2610. if (MI_IS_SESSION_IMAGE_ADDRESS (BasedAddress)) {
  2611. ASSERT (MiSessionLookupImage (BasedAddress) == NULL);
  2612. }
  2613. #endif
  2614. if (MmSnapUnloads) {
  2615. #if 0
  2616. PVOID StillQueued;
  2617. StillQueued = KeCheckForTimer (DataTableEntry->DllBase,
  2618. DataTableEntry->SizeOfImage);
  2619. if (StillQueued != NULL) {
  2620. KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
  2621. 0x18,
  2622. (ULONG_PTR)StillQueued,
  2623. (ULONG_PTR)-1,
  2624. (ULONG_PTR)DataTableEntry->DllBase);
  2625. }
  2626. StillQueued = ExpCheckForResource (DataTableEntry->DllBase,
  2627. DataTableEntry->SizeOfImage);
  2628. if (StillQueued != NULL) {
  2629. KeBugCheckEx (DRIVER_VERIFIER_DETECTED_VIOLATION,
  2630. 0x19,
  2631. (ULONG_PTR)StillQueued,
  2632. (ULONG_PTR)-1,
  2633. (ULONG_PTR)DataTableEntry->DllBase);
  2634. }
  2635. #endif
  2636. }
  2637. if (MmVerifierData.Level & DRIVER_VERIFIER_DEADLOCK_DETECTION) {
  2638. VerifierDeadlockFreePool (DataTableEntry->DllBase, DataTableEntry->SizeOfImage);
  2639. }
  2640. if (DataTableEntry->Flags & LDRP_IMAGE_VERIFYING) {
  2641. MiVerifyingDriverUnloading (DataTableEntry);
  2642. }
  2643. if (MiActiveVerifierThunks != 0) {
  2644. MiVerifierCheckThunks (DataTableEntry);
  2645. }
  2646. //
  2647. // Unload symbols from debugger.
  2648. //
  2649. if (DataTableEntry->Flags & LDRP_DEBUG_SYMBOLS_LOADED) {
  2650. //
  2651. // TEMP TEMP TEMP rip out when debugger converted
  2652. //
  2653. ANSI_STRING AnsiName;
  2654. Status = RtlUnicodeStringToAnsiString (&AnsiName,
  2655. &DataTableEntry->BaseDllName,
  2656. TRUE);
  2657. if (NT_SUCCESS (Status)) {
  2658. DbgUnLoadImageSymbols (&AnsiName,
  2659. BasedAddress,
  2660. (ULONG)-1);
  2661. RtlFreeAnsiString (&AnsiName);
  2662. }
  2663. }
  2664. //
  2665. // No unload can happen till after Mm has finished Phase 1 initialization.
  2666. // Therefore, large pages are already in effect (if this platform supports
  2667. // it).
  2668. //
  2669. if (ViewDeleted == FALSE) {
  2670. NumberOfPtes = DataTableEntry->SizeOfImage >> PAGE_SHIFT;
  2671. if (MmSnapUnloads) {
  2672. MiRememberUnloadedDriver (&DataTableEntry->BaseDllName,
  2673. BasedAddress,
  2674. (ULONG)(NumberOfPtes << PAGE_SHIFT));
  2675. }
  2676. if (DataTableEntry->Flags & LDRP_SYSTEM_MAPPED) {
  2677. PointerPte = MiGetPteAddress (BasedAddress);
  2678. PagesRequired = MiDeleteSystemPagableVm (PointerPte,
  2679. NumberOfPtes,
  2680. ZeroKernelPte,
  2681. FALSE,
  2682. &ResidentPages);
  2683. MmTotalSystemDriverPages -= (ULONG)(PagesRequired - ResidentPages);
  2684. //
  2685. // Note that drivers loaded at boot that have not been relocated
  2686. // have no system PTEs or commit charged.
  2687. //
  2688. MiReleaseSystemPtes (PointerPte,
  2689. (ULONG)NumberOfPtes,
  2690. SystemPteSpace);
  2691. MiReturnResidentAvailable (ResidentPages, 21);
  2692. //
  2693. // Only return commitment for drivers that weren't loaded by the
  2694. // boot loader.
  2695. //
  2696. if (DataTableEntry->SectionPointer != NULL) {
  2697. MiReturnCommitment (PagesRequired);
  2698. MM_TRACK_COMMIT (MM_DBG_COMMIT_RETURN_DRIVER_UNLOAD1, PagesRequired);
  2699. InterlockedExchangeAdd ((PLONG)&MmDriverCommit,
  2700. (LONG) (0 - PagesRequired));
  2701. }
  2702. }
  2703. else {
  2704. //
  2705. // This must be a boot driver that was not relocated into
  2706. // system PTEs. If large or super pages are enabled, the
  2707. // image pages must be freed without referencing the
  2708. // non-existent page table pages. If large/super pages are
  2709. // not enabled, note that system PTEs were not used to map the
  2710. // image and thus, cannot be freed.
  2711. //
  2712. // This is further complicated by the fact that the INIT and/or
  2713. // discardable portions of these images may have already been freed.
  2714. //
  2715. }
  2716. }
  2717. //
  2718. // Search the loaded module list for the data table entry that describes
  2719. // the DLL that was just unloaded. It is possible an entry is not in the
  2720. // list if a failure occurred at a point in loading the DLL just before
  2721. // the data table entry was generated.
  2722. //
  2723. if (DataTableEntry->InLoadOrderLinks.Flink != NULL) {
  2724. MiProcessLoaderEntry (DataTableEntry, FALSE);
  2725. MustFree = TRUE;
  2726. }
  2727. else {
  2728. MustFree = FALSE;
  2729. }
  2730. //
  2731. // Handle unloading of any dependent DLLs that we loaded automatically
  2732. // for this image.
  2733. //
  2734. MiDereferenceImports ((PLOAD_IMPORTS)DataTableEntry->LoadedImports);
  2735. MiClearImports (DataTableEntry);
  2736. //
  2737. // Free this loader entry.
  2738. //
  2739. if (MustFree == TRUE) {
  2740. if (DataTableEntry->FullDllName.Buffer != NULL) {
  2741. ExFreePool (DataTableEntry->FullDllName.Buffer);
  2742. }
  2743. //
  2744. // Dereference the section object if there is one.
  2745. // There should only be one for win32k.sys and Hydra session images.
  2746. //
  2747. if ((DataTableEntry->SectionPointer != NULL) &&
  2748. (DataTableEntry->SectionPointer != (PVOID)-1)) {
  2749. ObDereferenceObject (DataTableEntry->SectionPointer);
  2750. }
  2751. ExFreePool((PVOID)DataTableEntry);
  2752. }
  2753. KeReleaseMutant (&MmSystemLoadLock, 1, FALSE, FALSE);
  2754. KeLeaveCriticalRegionThread (CurrentThread);
  2755. PERFINFO_IMAGE_UNLOAD(BasedAddress);
  2756. return STATUS_SUCCESS;
  2757. }
  2758. NTSTATUS
  2759. MiBuildImportsForBootDrivers(
  2760. VOID
  2761. )
  2762. /*++
  2763. Routine Description:
  2764. Construct an import list chain for boot-loaded drivers.
  2765. If this cannot be done for an entry, its chain is set to LOADED_AT_BOOT.
  2766. If a chain can be successfully built, then this driver's DLLs
  2767. will be automatically unloaded if this driver goes away (provided
  2768. no other driver is also using them). Otherwise, on driver unload,
  2769. its dependent DLLs would have to be explicitly unloaded.
  2770. Note that the incoming LoadCount values are not correct and thus, they
  2771. are reinitialized here.
  2772. Arguments:
  2773. None.
  2774. Return Value:
  2775. Various NTSTATUS codes.
  2776. --*/
  2777. {
  2778. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  2779. PLIST_ENTRY NextEntry;
  2780. PKLDR_DATA_TABLE_ENTRY DataTableEntry2;
  2781. PLIST_ENTRY NextEntry2;
  2782. ULONG i;
  2783. ULONG j;
  2784. ULONG ImageCount;
  2785. PVOID *ImageReferences;
  2786. PVOID LastImageReference;
  2787. PULONG_PTR ImportThunk;
  2788. ULONG_PTR BaseAddress;
  2789. ULONG_PTR LastAddress;
  2790. ULONG ImportSize;
  2791. ULONG ImportListSize;
  2792. PLOAD_IMPORTS ImportList;
  2793. LOGICAL UndoEverything;
  2794. PKLDR_DATA_TABLE_ENTRY KernelDataTableEntry;
  2795. PKLDR_DATA_TABLE_ENTRY HalDataTableEntry;
  2796. UNICODE_STRING KernelString;
  2797. UNICODE_STRING HalString;
  2798. PAGED_CODE();
  2799. ImageCount = 0;
  2800. KernelDataTableEntry = NULL;
  2801. HalDataTableEntry = NULL;
  2802. RtlInitUnicodeString (&KernelString, (const PUSHORT)L"ntoskrnl.exe");
  2803. RtlInitUnicodeString (&HalString, (const PUSHORT)L"hal.dll");
  2804. NextEntry = PsLoadedModuleList.Flink;
  2805. while (NextEntry != &PsLoadedModuleList) {
  2806. DataTableEntry = CONTAINING_RECORD(NextEntry,
  2807. KLDR_DATA_TABLE_ENTRY,
  2808. InLoadOrderLinks);
  2809. if (RtlEqualUnicodeString (&KernelString,
  2810. &DataTableEntry->BaseDllName,
  2811. TRUE)) {
  2812. KernelDataTableEntry = CONTAINING_RECORD(NextEntry,
  2813. KLDR_DATA_TABLE_ENTRY,
  2814. InLoadOrderLinks);
  2815. }
  2816. else if (RtlEqualUnicodeString (&HalString,
  2817. &DataTableEntry->BaseDllName,
  2818. TRUE)) {
  2819. HalDataTableEntry = CONTAINING_RECORD(NextEntry,
  2820. KLDR_DATA_TABLE_ENTRY,
  2821. InLoadOrderLinks);
  2822. }
  2823. //
  2824. // Initialize these properly so error recovery is simplified.
  2825. //
  2826. if (DataTableEntry->Flags & LDRP_DRIVER_DEPENDENT_DLL) {
  2827. if ((DataTableEntry == HalDataTableEntry) || (DataTableEntry == KernelDataTableEntry)) {
  2828. DataTableEntry->LoadCount = 1;
  2829. }
  2830. else {
  2831. DataTableEntry->LoadCount = 0;
  2832. }
  2833. }
  2834. else {
  2835. DataTableEntry->LoadCount = 1;
  2836. }
  2837. DataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  2838. ImageCount += 1;
  2839. NextEntry = NextEntry->Flink;
  2840. }
  2841. if (KernelDataTableEntry == NULL || HalDataTableEntry == NULL) {
  2842. return STATUS_NOT_FOUND;
  2843. }
  2844. ImageReferences = (PVOID *) ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  2845. ImageCount * sizeof (PVOID),
  2846. 'TDmM');
  2847. if (ImageReferences == NULL) {
  2848. return STATUS_INSUFFICIENT_RESOURCES;
  2849. }
  2850. UndoEverything = FALSE;
  2851. NextEntry = PsLoadedModuleList.Flink;
  2852. for ( ; NextEntry != &PsLoadedModuleList; NextEntry = NextEntry->Flink) {
  2853. DataTableEntry = CONTAINING_RECORD(NextEntry,
  2854. KLDR_DATA_TABLE_ENTRY,
  2855. InLoadOrderLinks);
  2856. ImportThunk = (PULONG_PTR)RtlImageDirectoryEntryToData(
  2857. DataTableEntry->DllBase,
  2858. TRUE,
  2859. IMAGE_DIRECTORY_ENTRY_IAT,
  2860. &ImportSize);
  2861. if (ImportThunk == NULL) {
  2862. DataTableEntry->LoadedImports = NO_IMPORTS_USED;
  2863. continue;
  2864. }
  2865. RtlZeroMemory (ImageReferences, ImageCount * sizeof (PVOID));
  2866. ImportSize /= sizeof(PULONG_PTR);
  2867. BaseAddress = 0;
  2868. //
  2869. // Initializing these locals is not needed for correctness, but
  2870. // without it the compiler cannot compile this code W4 to check
  2871. // for use of uninitialized variables.
  2872. //
  2873. j = 0;
  2874. LastAddress = 0;
  2875. for (i = 0; i < ImportSize; i += 1, ImportThunk += 1) {
  2876. //
  2877. // Check the hint first.
  2878. //
  2879. if (BaseAddress != 0) {
  2880. if (*ImportThunk >= BaseAddress && *ImportThunk < LastAddress) {
  2881. ASSERT (ImageReferences[j]);
  2882. continue;
  2883. }
  2884. }
  2885. j = 0;
  2886. NextEntry2 = PsLoadedModuleList.Flink;
  2887. while (NextEntry2 != &PsLoadedModuleList) {
  2888. DataTableEntry2 = CONTAINING_RECORD(NextEntry2,
  2889. KLDR_DATA_TABLE_ENTRY,
  2890. InLoadOrderLinks);
  2891. BaseAddress = (ULONG_PTR) DataTableEntry2->DllBase;
  2892. LastAddress = BaseAddress + DataTableEntry2->SizeOfImage;
  2893. if (*ImportThunk >= BaseAddress && *ImportThunk < LastAddress) {
  2894. ImageReferences[j] = DataTableEntry2;
  2895. break;
  2896. }
  2897. NextEntry2 = NextEntry2->Flink;
  2898. j += 1;
  2899. }
  2900. if (*ImportThunk < BaseAddress || *ImportThunk >= LastAddress) {
  2901. if (*ImportThunk) {
  2902. #if DBG
  2903. DbgPrint ("MM: broken import linkage %p %p %p\n",
  2904. DataTableEntry,
  2905. ImportThunk,
  2906. *ImportThunk);
  2907. DbgBreakPoint ();
  2908. #endif
  2909. UndoEverything = TRUE;
  2910. goto finished;
  2911. }
  2912. BaseAddress = 0;
  2913. }
  2914. }
  2915. ImportSize = 0;
  2916. //
  2917. // Initializing LastImageReference is not needed for correctness, but
  2918. // without it the compiler cannot compile this code W4 to check
  2919. // for use of uninitialized variables.
  2920. //
  2921. LastImageReference = NULL;
  2922. for (i = 0; i < ImageCount; i += 1) {
  2923. if ((ImageReferences[i] != NULL) &&
  2924. (ImageReferences[i] != KernelDataTableEntry) &&
  2925. (ImageReferences[i] != HalDataTableEntry)) {
  2926. LastImageReference = ImageReferences[i];
  2927. ImportSize += 1;
  2928. }
  2929. }
  2930. if (ImportSize == 0) {
  2931. DataTableEntry->LoadedImports = NO_IMPORTS_USED;
  2932. }
  2933. else if (ImportSize == 1) {
  2934. #if DBG_SYSLOAD
  2935. DbgPrint("driver %wZ imports %wZ\n",
  2936. &DataTableEntry->FullDllName,
  2937. &((PKLDR_DATA_TABLE_ENTRY)LastImageReference)->FullDllName);
  2938. #endif
  2939. DataTableEntry->LoadedImports = POINTER_TO_SINGLE_ENTRY (LastImageReference);
  2940. ((PKLDR_DATA_TABLE_ENTRY)LastImageReference)->LoadCount += 1;
  2941. }
  2942. else {
  2943. #if DBG_SYSLOAD
  2944. DbgPrint("driver %wZ imports many\n", &DataTableEntry->FullDllName);
  2945. #endif
  2946. ImportListSize = ImportSize * sizeof(PVOID) + sizeof(SIZE_T);
  2947. ImportList = (PLOAD_IMPORTS) ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  2948. ImportListSize,
  2949. 'TDmM');
  2950. if (ImportList == NULL) {
  2951. UndoEverything = TRUE;
  2952. break;
  2953. }
  2954. ImportList->Count = ImportSize;
  2955. j = 0;
  2956. for (i = 0; i < ImageCount; i += 1) {
  2957. if ((ImageReferences[i] != NULL) &&
  2958. (ImageReferences[i] != KernelDataTableEntry) &&
  2959. (ImageReferences[i] != HalDataTableEntry)) {
  2960. #if DBG_SYSLOAD
  2961. DbgPrint("driver %wZ imports %wZ\n",
  2962. &DataTableEntry->FullDllName,
  2963. &((PKLDR_DATA_TABLE_ENTRY)ImageReferences[i])->FullDllName);
  2964. #endif
  2965. ImportList->Entry[j] = ImageReferences[i];
  2966. ((PKLDR_DATA_TABLE_ENTRY)ImageReferences[i])->LoadCount += 1;
  2967. j += 1;
  2968. }
  2969. }
  2970. ASSERT (j == ImportSize);
  2971. DataTableEntry->LoadedImports = ImportList;
  2972. }
  2973. #if DBG_SYSLOAD
  2974. DbgPrint("\n");
  2975. #endif
  2976. }
  2977. finished:
  2978. ExFreePool ((PVOID)ImageReferences);
  2979. //
  2980. // The kernel and HAL are never unloaded.
  2981. //
  2982. if ((KernelDataTableEntry->LoadedImports != NO_IMPORTS_USED) &&
  2983. (!POINTER_TO_SINGLE_ENTRY(KernelDataTableEntry->LoadedImports))) {
  2984. ExFreePool ((PVOID)KernelDataTableEntry->LoadedImports);
  2985. }
  2986. if ((HalDataTableEntry->LoadedImports != NO_IMPORTS_USED) &&
  2987. (!POINTER_TO_SINGLE_ENTRY(HalDataTableEntry->LoadedImports))) {
  2988. ExFreePool ((PVOID)HalDataTableEntry->LoadedImports);
  2989. }
  2990. KernelDataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  2991. HalDataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  2992. if (UndoEverything == TRUE) {
  2993. #if DBG_SYSLOAD
  2994. DbgPrint("driver %wZ import rebuild failed\n",
  2995. &DataTableEntry->FullDllName);
  2996. DbgBreakPoint();
  2997. #endif
  2998. //
  2999. // An error occurred and this is an all or nothing operation so
  3000. // roll everything back.
  3001. //
  3002. NextEntry = PsLoadedModuleList.Flink;
  3003. while (NextEntry != &PsLoadedModuleList) {
  3004. DataTableEntry = CONTAINING_RECORD(NextEntry,
  3005. KLDR_DATA_TABLE_ENTRY,
  3006. InLoadOrderLinks);
  3007. ImportList = DataTableEntry->LoadedImports;
  3008. if (ImportList == LOADED_AT_BOOT || ImportList == NO_IMPORTS_USED ||
  3009. SINGLE_ENTRY(ImportList)) {
  3010. NOTHING;
  3011. }
  3012. else {
  3013. ExFreePool (ImportList);
  3014. }
  3015. DataTableEntry->LoadedImports = (PVOID)LOADED_AT_BOOT;
  3016. DataTableEntry->LoadCount = 1;
  3017. NextEntry = NextEntry->Flink;
  3018. }
  3019. return STATUS_INSUFFICIENT_RESOURCES;
  3020. }
  3021. return STATUS_SUCCESS;
  3022. }
  3023. LOGICAL
  3024. MiCallDllUnloadAndUnloadDll(
  3025. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  3026. )
  3027. /*++
  3028. Routine Description:
  3029. All the references from other drivers to this DLL have been cleared.
  3030. The only remaining issue is that this DLL must support being unloaded.
  3031. This means having no outstanding DPCs, allocated pool, etc.
  3032. If the DLL has an unload routine that returns SUCCESS, then we clean
  3033. it up and free up its memory now.
  3034. Note this routine is NEVER called for drivers - only for DLLs that were
  3035. loaded due to import references from various drivers.
  3036. Arguments:
  3037. DataTableEntry - provided for the DLL.
  3038. Return Value:
  3039. TRUE if the DLL was successfully unloaded, FALSE if not.
  3040. --*/
  3041. {
  3042. PMM_DLL_UNLOAD Func;
  3043. NTSTATUS Status;
  3044. LOGICAL Unloaded;
  3045. PAGED_CODE();
  3046. Unloaded = FALSE;
  3047. Func = (PMM_DLL_UNLOAD) (ULONG_PTR) MiLocateExportName (DataTableEntry->DllBase, "DllUnload");
  3048. if (Func) {
  3049. //
  3050. // The unload function was found in the DLL so unload it now.
  3051. //
  3052. Status = Func();
  3053. if (NT_SUCCESS(Status)) {
  3054. //
  3055. // Set up the reference count so the import DLL looks like a regular
  3056. // driver image is being unloaded.
  3057. //
  3058. ASSERT (DataTableEntry->LoadCount == 0);
  3059. DataTableEntry->LoadCount = 1;
  3060. MmUnloadSystemImage ((PVOID)DataTableEntry);
  3061. Unloaded = TRUE;
  3062. }
  3063. }
  3064. return Unloaded;
  3065. }
  3066. PVOID
  3067. MiLocateExportName (
  3068. IN PVOID DllBase,
  3069. IN PCHAR FunctionName
  3070. )
  3071. /*++
  3072. Routine Description:
  3073. This function is invoked to locate a function name in an export directory.
  3074. Arguments:
  3075. DllBase - Supplies the image base.
  3076. FunctionName - Supplies the the name to be located.
  3077. Return Value:
  3078. The address of the located function or NULL.
  3079. --*/
  3080. {
  3081. PVOID Func;
  3082. PULONG NameTableBase;
  3083. PUSHORT NameOrdinalTableBase;
  3084. PIMAGE_EXPORT_DIRECTORY ExportDirectory;
  3085. PULONG Addr;
  3086. ULONG ExportSize;
  3087. ULONG Low;
  3088. ULONG Middle;
  3089. ULONG High;
  3090. LONG Result;
  3091. USHORT OrdinalNumber;
  3092. PAGED_CODE();
  3093. Func = NULL;
  3094. //
  3095. // Locate the DLL's export directory.
  3096. //
  3097. ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(
  3098. DllBase,
  3099. TRUE,
  3100. IMAGE_DIRECTORY_ENTRY_EXPORT,
  3101. &ExportSize
  3102. );
  3103. if (ExportDirectory) {
  3104. NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
  3105. NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
  3106. //
  3107. // Look in the export name table for the specified function name.
  3108. //
  3109. Low = 0;
  3110. High = ExportDirectory->NumberOfNames - 1;
  3111. //
  3112. // Initializing Middle is not needed for correctness, but without it
  3113. // the compiler cannot compile this code W4 to check for use of
  3114. // uninitialized variables.
  3115. //
  3116. Middle = 0;
  3117. while (High >= Low && (LONG)High >= 0) {
  3118. //
  3119. // Compute the next probe index and compare the export name entry
  3120. // with the specified function name.
  3121. //
  3122. Middle = (Low + High) >> 1;
  3123. Result = strcmp(FunctionName,
  3124. (PCHAR)((PCHAR)DllBase + NameTableBase[Middle]));
  3125. if (Result < 0) {
  3126. High = Middle - 1;
  3127. }
  3128. else if (Result > 0) {
  3129. Low = Middle + 1;
  3130. }
  3131. else {
  3132. break;
  3133. }
  3134. }
  3135. //
  3136. // If the high index is less than the low index, then a matching table
  3137. // entry was not found. Otherwise, get the ordinal number from the
  3138. // ordinal table and location the function address.
  3139. //
  3140. if ((LONG)High >= (LONG)Low) {
  3141. OrdinalNumber = NameOrdinalTableBase[Middle];
  3142. Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
  3143. Func = (PVOID)((ULONG_PTR)DllBase + Addr[OrdinalNumber]);
  3144. //
  3145. // If the function address is w/in range of the export directory,
  3146. // then the function is forwarded, which is not allowed, so ignore
  3147. // it.
  3148. //
  3149. if ((ULONG_PTR)Func > (ULONG_PTR)ExportDirectory &&
  3150. (ULONG_PTR)Func < ((ULONG_PTR)ExportDirectory + ExportSize)) {
  3151. Func = NULL;
  3152. }
  3153. }
  3154. }
  3155. return Func;
  3156. }
  3157. NTSTATUS
  3158. MiDereferenceImports (
  3159. IN PLOAD_IMPORTS ImportList
  3160. )
  3161. /*++
  3162. Routine Description:
  3163. Decrement the reference count on each DLL specified in the image import
  3164. list. If any DLL's reference count reaches zero, then free the DLL.
  3165. No locks may be held on entry as MmUnloadSystemImage may be called.
  3166. The parameter list is freed here as well.
  3167. Arguments:
  3168. ImportList - Supplies the list of DLLs to dereference.
  3169. Return Value:
  3170. Status of the dereference operation.
  3171. --*/
  3172. {
  3173. ULONG i;
  3174. LOGICAL Unloaded;
  3175. PVOID SavedImports;
  3176. LOAD_IMPORTS SingleTableEntry;
  3177. PKLDR_DATA_TABLE_ENTRY ImportTableEntry;
  3178. PAGED_CODE();
  3179. if (ImportList == LOADED_AT_BOOT || ImportList == NO_IMPORTS_USED) {
  3180. return STATUS_SUCCESS;
  3181. }
  3182. if (SINGLE_ENTRY(ImportList)) {
  3183. SingleTableEntry.Count = 1;
  3184. SingleTableEntry.Entry[0] = SINGLE_ENTRY_TO_POINTER(ImportList);
  3185. ImportList = &SingleTableEntry;
  3186. }
  3187. for (i = 0; i < ImportList->Count && ImportList->Entry[i]; i += 1) {
  3188. ImportTableEntry = ImportList->Entry[i];
  3189. if (ImportTableEntry->LoadedImports == (PVOID)LOADED_AT_BOOT) {
  3190. //
  3191. // Skip this one - it was loaded by ntldr.
  3192. //
  3193. continue;
  3194. }
  3195. #if DBG
  3196. {
  3197. ULONG ImageCount;
  3198. PLIST_ENTRY NextEntry;
  3199. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  3200. //
  3201. // Assert that the first 2 entries are never dereferenced as
  3202. // unloading the kernel or HAL would be fatal.
  3203. //
  3204. NextEntry = PsLoadedModuleList.Flink;
  3205. ImageCount = 0;
  3206. while (NextEntry != &PsLoadedModuleList && ImageCount < 2) {
  3207. DataTableEntry = CONTAINING_RECORD(NextEntry,
  3208. KLDR_DATA_TABLE_ENTRY,
  3209. InLoadOrderLinks);
  3210. ASSERT (ImportTableEntry != DataTableEntry);
  3211. ASSERT (DataTableEntry->LoadCount == 1);
  3212. NextEntry = NextEntry->Flink;
  3213. ImageCount += 1;
  3214. }
  3215. }
  3216. #endif
  3217. ASSERT (ImportTableEntry->LoadCount >= 1);
  3218. ImportTableEntry->LoadCount -= 1;
  3219. if (ImportTableEntry->LoadCount == 0) {
  3220. //
  3221. // Unload this dependent DLL - we only do this to non-referenced
  3222. // non-boot-loaded drivers. Stop the import list recursion prior
  3223. // to unloading - we know we're done at this point.
  3224. //
  3225. // Note we can continue on afterwards without restarting
  3226. // regardless of which locks get released and reacquired
  3227. // because this chain is private.
  3228. //
  3229. SavedImports = ImportTableEntry->LoadedImports;
  3230. ImportTableEntry->LoadedImports = (PVOID)NO_IMPORTS_USED;
  3231. Unloaded = MiCallDllUnloadAndUnloadDll ((PVOID)ImportTableEntry);
  3232. if (Unloaded == TRUE) {
  3233. //
  3234. // This DLL was unloaded so recurse through its imports and
  3235. // attempt to unload all of those too.
  3236. //
  3237. MiDereferenceImports ((PLOAD_IMPORTS)SavedImports);
  3238. if ((SavedImports != (PVOID)LOADED_AT_BOOT) &&
  3239. (SavedImports != (PVOID)NO_IMPORTS_USED) &&
  3240. (!SINGLE_ENTRY(SavedImports))) {
  3241. ExFreePool (SavedImports);
  3242. }
  3243. }
  3244. else {
  3245. ImportTableEntry->LoadedImports = SavedImports;
  3246. }
  3247. }
  3248. }
  3249. return STATUS_SUCCESS;
  3250. }
  3251. NTSTATUS
  3252. MiResolveImageReferences (
  3253. PVOID ImageBase,
  3254. IN PUNICODE_STRING ImageFileDirectory,
  3255. IN PUNICODE_STRING NamePrefix OPTIONAL,
  3256. OUT PCHAR *MissingProcedureName,
  3257. OUT PWSTR *MissingDriverName,
  3258. OUT PLOAD_IMPORTS *LoadedImports
  3259. )
  3260. /*++
  3261. Routine Description:
  3262. This routine resolves the references from the newly loaded driver
  3263. to the kernel, HAL and other drivers.
  3264. Arguments:
  3265. ImageBase - Supplies the address of which the image header resides.
  3266. ImageFileDirectory - Supplies the directory to load referenced DLLs.
  3267. Return Value:
  3268. Status of the image reference resolution.
  3269. --*/
  3270. {
  3271. PCHAR MissingProcedureStorageArea;
  3272. PVOID ImportBase;
  3273. ULONG ImportSize;
  3274. ULONG ImportListSize;
  3275. ULONG Count;
  3276. ULONG i;
  3277. PIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
  3278. PIMAGE_IMPORT_DESCRIPTOR Imp;
  3279. NTSTATUS st;
  3280. ULONG ExportSize;
  3281. PIMAGE_EXPORT_DIRECTORY ExportDirectory;
  3282. PIMAGE_THUNK_DATA NameThunk;
  3283. PIMAGE_THUNK_DATA AddrThunk;
  3284. PSZ ImportName;
  3285. PLIST_ENTRY NextEntry;
  3286. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  3287. PKLDR_DATA_TABLE_ENTRY SingleEntry;
  3288. ANSI_STRING AnsiString;
  3289. UNICODE_STRING ImportName_U;
  3290. UNICODE_STRING ImportDescriptorName_U;
  3291. UNICODE_STRING DllToLoad;
  3292. UNICODE_STRING DllToLoad2;
  3293. PVOID Section;
  3294. PVOID BaseAddress;
  3295. LOGICAL PrefixedNameAllocated;
  3296. LOGICAL ReferenceImport;
  3297. ULONG LinkWin32k = 0;
  3298. ULONG LinkNonWin32k = 0;
  3299. PLOAD_IMPORTS ImportList;
  3300. PLOAD_IMPORTS CompactedImportList;
  3301. LOGICAL Loaded;
  3302. UNICODE_STRING DriverDirectory;
  3303. PAGED_CODE();
  3304. *LoadedImports = NO_IMPORTS_USED;
  3305. MissingProcedureStorageArea = *MissingProcedureName;
  3306. ImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(
  3307. ImageBase,
  3308. TRUE,
  3309. IMAGE_DIRECTORY_ENTRY_IMPORT,
  3310. &ImportSize);
  3311. if (ImportDescriptor == NULL) {
  3312. return STATUS_SUCCESS;
  3313. }
  3314. // Count the number of imports so we can allocate enough room to
  3315. // store them all chained off this module's LDR_DATA_TABLE_ENTRY.
  3316. //
  3317. Count = 0;
  3318. for (Imp = ImportDescriptor; Imp->Name && Imp->OriginalFirstThunk; Imp += 1) {
  3319. Count += 1;
  3320. }
  3321. if (Count != 0) {
  3322. ImportListSize = Count * sizeof(PVOID) + sizeof(SIZE_T);
  3323. ImportList = (PLOAD_IMPORTS) ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  3324. ImportListSize,
  3325. 'TDmM');
  3326. //
  3327. // Zero it so we can recover gracefully if we fail in the middle.
  3328. // If the allocation failed, just don't build the import list.
  3329. //
  3330. if (ImportList != NULL) {
  3331. RtlZeroMemory (ImportList, ImportListSize);
  3332. ImportList->Count = Count;
  3333. }
  3334. }
  3335. else {
  3336. ImportList = NULL;
  3337. }
  3338. Count = 0;
  3339. while (ImportDescriptor->Name && ImportDescriptor->OriginalFirstThunk) {
  3340. ImportName = (PSZ)((PCHAR)ImageBase + ImportDescriptor->Name);
  3341. //
  3342. // A driver can link with win32k.sys if and only if it is a GDI
  3343. // driver.
  3344. // Also display drivers can only link to win32k.sys (and lego ...).
  3345. //
  3346. // So if we get a driver that links to win32k.sys and has more
  3347. // than one set of imports, we will fail to load it.
  3348. //
  3349. LinkWin32k = LinkWin32k |
  3350. (!_strnicmp(ImportName, "win32k", sizeof("win32k") - 1));
  3351. //
  3352. // We don't want to count coverage, win32k and irt (lego) since
  3353. // display drivers CAN link against these.
  3354. //
  3355. LinkNonWin32k = LinkNonWin32k |
  3356. ((_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) &&
  3357. (_strnicmp(ImportName, "dxapi", sizeof("dxapi") - 1)) &&
  3358. (_strnicmp(ImportName, "coverage", sizeof("coverage") - 1)) &&
  3359. (_strnicmp(ImportName, "irt", sizeof("irt") - 1)));
  3360. if (LinkNonWin32k && LinkWin32k) {
  3361. MiDereferenceImports (ImportList);
  3362. if (ImportList) {
  3363. ExFreePool (ImportList);
  3364. }
  3365. return (STATUS_PROCEDURE_NOT_FOUND);
  3366. }
  3367. if ((!_strnicmp(ImportName, "ntdll", sizeof("ntdll") - 1)) ||
  3368. (!_strnicmp(ImportName, "winsrv", sizeof("winsrv") - 1)) ||
  3369. (!_strnicmp(ImportName, "advapi32", sizeof("advapi32") - 1)) ||
  3370. (!_strnicmp(ImportName, "kernel32", sizeof("kernel32") - 1)) ||
  3371. (!_strnicmp(ImportName, "user32", sizeof("user32") - 1)) ||
  3372. (!_strnicmp(ImportName, "gdi32", sizeof("gdi32") - 1)) ) {
  3373. MiDereferenceImports (ImportList);
  3374. if (ImportList) {
  3375. ExFreePool (ImportList);
  3376. }
  3377. return (STATUS_PROCEDURE_NOT_FOUND);
  3378. }
  3379. if ((!_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1)) ||
  3380. (!_strnicmp(ImportName, "win32k", sizeof("win32k") - 1)) ||
  3381. (!_strnicmp(ImportName, "hal", sizeof("hal") - 1))) {
  3382. //
  3383. // These imports don't get refcounted because we don't
  3384. // ever want to unload them.
  3385. //
  3386. ReferenceImport = FALSE;
  3387. }
  3388. else {
  3389. ReferenceImport = TRUE;
  3390. }
  3391. RtlInitAnsiString (&AnsiString, ImportName);
  3392. st = RtlAnsiStringToUnicodeString (&ImportName_U, &AnsiString, TRUE);
  3393. if (!NT_SUCCESS(st)) {
  3394. MiDereferenceImports (ImportList);
  3395. if (ImportList != NULL) {
  3396. ExFreePool (ImportList);
  3397. }
  3398. return st;
  3399. }
  3400. if (NamePrefix &&
  3401. (_strnicmp(ImportName, "ntoskrnl", sizeof("ntoskrnl") - 1) &&
  3402. _strnicmp(ImportName, "hal", sizeof("hal") - 1))) {
  3403. ImportDescriptorName_U.MaximumLength = (USHORT)(ImportName_U.Length + NamePrefix->Length);
  3404. ImportDescriptorName_U.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  3405. ImportDescriptorName_U.MaximumLength,
  3406. 'TDmM');
  3407. if (!ImportDescriptorName_U.Buffer) {
  3408. RtlFreeUnicodeString (&ImportName_U);
  3409. MiDereferenceImports (ImportList);
  3410. if (ImportList != NULL) {
  3411. ExFreePool (ImportList);
  3412. }
  3413. return STATUS_INSUFFICIENT_RESOURCES;
  3414. }
  3415. ImportDescriptorName_U.Length = 0;
  3416. RtlAppendUnicodeStringToString(&ImportDescriptorName_U, NamePrefix);
  3417. RtlAppendUnicodeStringToString(&ImportDescriptorName_U, &ImportName_U);
  3418. PrefixedNameAllocated = TRUE;
  3419. }
  3420. else {
  3421. ImportDescriptorName_U = ImportName_U;
  3422. PrefixedNameAllocated = FALSE;
  3423. }
  3424. Loaded = FALSE;
  3425. ReCheck:
  3426. NextEntry = PsLoadedModuleList.Flink;
  3427. ImportBase = NULL;
  3428. //
  3429. // Initializing DataTableEntry is not needed for correctness
  3430. // but without it the compiler cannot compile this code
  3431. // W4 to check for use of uninitialized variables.
  3432. //
  3433. DataTableEntry = NULL;
  3434. while (NextEntry != &PsLoadedModuleList) {
  3435. DataTableEntry = CONTAINING_RECORD(NextEntry,
  3436. KLDR_DATA_TABLE_ENTRY,
  3437. InLoadOrderLinks);
  3438. if (RtlEqualUnicodeString (&ImportDescriptorName_U,
  3439. &DataTableEntry->BaseDllName,
  3440. TRUE)) {
  3441. ImportBase = DataTableEntry->DllBase;
  3442. //
  3443. // Only bump the LoadCount if this thread did not initiate
  3444. // the load below. If this thread initiated the load, then
  3445. // the LoadCount has already been bumped as part of the
  3446. // load - we only want to increment it here if we are
  3447. // "attaching" to a previously loaded DLL.
  3448. //
  3449. if ((Loaded == FALSE) && (ReferenceImport == TRUE)) {
  3450. //
  3451. // Only increment the load count on the import if it is not
  3452. // circular (ie: the import is not from the original
  3453. // caller).
  3454. //
  3455. if ((DataTableEntry->Flags & LDRP_LOAD_IN_PROGRESS) == 0) {
  3456. DataTableEntry->LoadCount += 1;
  3457. }
  3458. }
  3459. break;
  3460. }
  3461. NextEntry = NextEntry->Flink;
  3462. }
  3463. if (ImportBase == NULL) {
  3464. //
  3465. // The DLL name was not located, attempt to load this dll.
  3466. //
  3467. DllToLoad.MaximumLength = (USHORT)(ImportName_U.Length +
  3468. ImageFileDirectory->Length +
  3469. sizeof(WCHAR));
  3470. DllToLoad.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  3471. DllToLoad.MaximumLength,
  3472. 'TDmM');
  3473. if (DllToLoad.Buffer) {
  3474. DllToLoad.Length = ImageFileDirectory->Length;
  3475. RtlCopyMemory (DllToLoad.Buffer,
  3476. ImageFileDirectory->Buffer,
  3477. ImageFileDirectory->Length);
  3478. RtlAppendStringToString ((PSTRING)&DllToLoad,
  3479. (PSTRING)&ImportName_U);
  3480. //
  3481. // Add NULL termination in case the load fails so the name
  3482. // can be returned as the PWSTR MissingDriverName.
  3483. //
  3484. DllToLoad.Buffer[(DllToLoad.MaximumLength - 1) / sizeof (WCHAR)] =
  3485. UNICODE_NULL;
  3486. st = MmLoadSystemImage (&DllToLoad,
  3487. NamePrefix,
  3488. NULL,
  3489. FALSE,
  3490. &Section,
  3491. &BaseAddress);
  3492. if (NT_SUCCESS(st)) {
  3493. //
  3494. // No need to keep the temporary name buffer around now
  3495. // that there is a loaded module list entry for this DLL.
  3496. //
  3497. ExFreePool (DllToLoad.Buffer);
  3498. }
  3499. else {
  3500. if ((st == STATUS_OBJECT_NAME_NOT_FOUND) &&
  3501. (NamePrefix == NULL) &&
  3502. (MI_IS_SESSION_ADDRESS (ImageBase))) {
  3503. #define DRIVERS_SUBDIR_NAME L"drivers\\"
  3504. DriverDirectory.Buffer = (const PUSHORT) DRIVERS_SUBDIR_NAME;
  3505. DriverDirectory.Length = sizeof (DRIVERS_SUBDIR_NAME) - sizeof (WCHAR);
  3506. DriverDirectory.MaximumLength = sizeof DRIVERS_SUBDIR_NAME;
  3507. //
  3508. // The DLL file was not located, attempt to load it
  3509. // from the drivers subdirectory. This makes it
  3510. // possible for drivers like win32k.sys to link to
  3511. // drivers that reside in the drivers subdirectory
  3512. // (like dxapi.sys).
  3513. //
  3514. DllToLoad2.MaximumLength = (USHORT)(ImportName_U.Length +
  3515. DriverDirectory.Length +
  3516. ImageFileDirectory->Length +
  3517. sizeof(WCHAR));
  3518. DllToLoad2.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  3519. DllToLoad2.MaximumLength,
  3520. 'TDmM');
  3521. if (DllToLoad2.Buffer) {
  3522. DllToLoad2.Length = ImageFileDirectory->Length;
  3523. RtlCopyMemory (DllToLoad2.Buffer,
  3524. ImageFileDirectory->Buffer,
  3525. ImageFileDirectory->Length);
  3526. RtlAppendStringToString ((PSTRING)&DllToLoad2,
  3527. (PSTRING)&DriverDirectory);
  3528. RtlAppendStringToString ((PSTRING)&DllToLoad2,
  3529. (PSTRING)&ImportName_U);
  3530. //
  3531. // Add NULL termination in case the load fails
  3532. // so the name can be returned as the PWSTR
  3533. // MissingDriverName.
  3534. //
  3535. DllToLoad2.Buffer[(DllToLoad2.MaximumLength - 1) / sizeof (WCHAR)] =
  3536. UNICODE_NULL;
  3537. st = MmLoadSystemImage (&DllToLoad2,
  3538. NULL,
  3539. NULL,
  3540. FALSE,
  3541. &Section,
  3542. &BaseAddress);
  3543. ExFreePool (DllToLoad.Buffer);
  3544. DllToLoad.Buffer = DllToLoad2.Buffer;
  3545. DllToLoad.Length = DllToLoad2.Length;
  3546. DllToLoad.MaximumLength = DllToLoad2.MaximumLength;
  3547. if (NT_SUCCESS(st)) {
  3548. ExFreePool (DllToLoad.Buffer);
  3549. goto LoadFinished;
  3550. }
  3551. }
  3552. else {
  3553. Section = NULL;
  3554. BaseAddress = NULL;
  3555. st = STATUS_INSUFFICIENT_RESOURCES;
  3556. goto LoadFinished;
  3557. }
  3558. }
  3559. //
  3560. // Return the temporary name buffer to our caller so
  3561. // the name of the DLL that failed to load can be displayed.
  3562. // Set the low bit of the pointer so our caller knows to
  3563. // free this buffer when he's done displaying it (as opposed
  3564. // to loaded module list entries which should not be freed).
  3565. //
  3566. *MissingDriverName = DllToLoad.Buffer;
  3567. *(PULONG)MissingDriverName |= 0x1;
  3568. //
  3569. // Set this to NULL so the hard error prints properly.
  3570. //
  3571. *MissingProcedureName = NULL;
  3572. }
  3573. }
  3574. else {
  3575. //
  3576. // Initializing Section and BaseAddress is not needed for
  3577. // correctness but without it the compiler cannot compile
  3578. // this code W4 to check for use of uninitialized variables.
  3579. //
  3580. Section = NULL;
  3581. BaseAddress = NULL;
  3582. st = STATUS_INSUFFICIENT_RESOURCES;
  3583. }
  3584. LoadFinished:
  3585. //
  3586. // Call any needed DLL initialization now.
  3587. //
  3588. if (NT_SUCCESS(st)) {
  3589. #if DBG
  3590. PLIST_ENTRY Entry;
  3591. #endif
  3592. PKLDR_DATA_TABLE_ENTRY TableEntry;
  3593. Loaded = TRUE;
  3594. TableEntry = (PKLDR_DATA_TABLE_ENTRY) Section;
  3595. ASSERT (BaseAddress == TableEntry->DllBase);
  3596. #if DBG
  3597. //
  3598. // Lookup the dll's table entry in the loaded module list.
  3599. // This is expected to always succeed.
  3600. //
  3601. Entry = PsLoadedModuleList.Blink;
  3602. while (Entry != &PsLoadedModuleList) {
  3603. TableEntry = CONTAINING_RECORD (Entry,
  3604. KLDR_DATA_TABLE_ENTRY,
  3605. InLoadOrderLinks);
  3606. if (BaseAddress == TableEntry->DllBase) {
  3607. ASSERT (TableEntry == (PKLDR_DATA_TABLE_ENTRY) Section);
  3608. break;
  3609. }
  3610. ASSERT (TableEntry != (PKLDR_DATA_TABLE_ENTRY) Section);
  3611. Entry = Entry->Blink;
  3612. }
  3613. ASSERT (Entry != &PsLoadedModuleList);
  3614. #endif
  3615. //
  3616. // Call the dll's initialization routine if it has
  3617. // one. This routine will reapply verifier thunks to
  3618. // any modules that link to this one if necessary.
  3619. //
  3620. st = MmCallDllInitialize (TableEntry, &PsLoadedModuleList);
  3621. //
  3622. // If the module could not be properly initialized,
  3623. // unload it.
  3624. //
  3625. if (!NT_SUCCESS(st)) {
  3626. MmUnloadSystemImage ((PVOID)TableEntry);
  3627. Loaded = FALSE;
  3628. }
  3629. }
  3630. if (!NT_SUCCESS(st)) {
  3631. RtlFreeUnicodeString (&ImportName_U);
  3632. if (PrefixedNameAllocated == TRUE) {
  3633. ExFreePool (ImportDescriptorName_U.Buffer);
  3634. }
  3635. MiDereferenceImports (ImportList);
  3636. if (ImportList != NULL) {
  3637. ExFreePool (ImportList);
  3638. }
  3639. return st;
  3640. }
  3641. goto ReCheck;
  3642. }
  3643. if ((ReferenceImport == TRUE) && (ImportList)) {
  3644. //
  3645. // Only add the image providing satisfying our imports to the
  3646. // import list if the reference is not circular (ie: the import
  3647. // is not from the original caller).
  3648. //
  3649. if ((DataTableEntry->Flags & LDRP_LOAD_IN_PROGRESS) == 0) {
  3650. ImportList->Entry[Count] = DataTableEntry;
  3651. Count += 1;
  3652. }
  3653. }
  3654. RtlFreeUnicodeString (&ImportName_U);
  3655. if (PrefixedNameAllocated) {
  3656. ExFreePool (ImportDescriptorName_U.Buffer);
  3657. }
  3658. *MissingDriverName = DataTableEntry->BaseDllName.Buffer;
  3659. ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(
  3660. ImportBase,
  3661. TRUE,
  3662. IMAGE_DIRECTORY_ENTRY_EXPORT,
  3663. &ExportSize
  3664. );
  3665. if (!ExportDirectory) {
  3666. MiDereferenceImports (ImportList);
  3667. if (ImportList) {
  3668. ExFreePool (ImportList);
  3669. }
  3670. return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
  3671. }
  3672. //
  3673. // Walk through the IAT and snap all the thunks.
  3674. //
  3675. if (ImportDescriptor->OriginalFirstThunk) {
  3676. NameThunk = (PIMAGE_THUNK_DATA)((PCHAR)ImageBase + (ULONG)ImportDescriptor->OriginalFirstThunk);
  3677. AddrThunk = (PIMAGE_THUNK_DATA)((PCHAR)ImageBase + (ULONG)ImportDescriptor->FirstThunk);
  3678. while (NameThunk->u1.AddressOfData) {
  3679. st = MiSnapThunk (ImportBase,
  3680. ImageBase,
  3681. NameThunk++,
  3682. AddrThunk++,
  3683. ExportDirectory,
  3684. ExportSize,
  3685. FALSE,
  3686. MissingProcedureName);
  3687. if (!NT_SUCCESS(st) ) {
  3688. MiDereferenceImports (ImportList);
  3689. if (ImportList) {
  3690. ExFreePool (ImportList);
  3691. }
  3692. return st;
  3693. }
  3694. *MissingProcedureName = MissingProcedureStorageArea;
  3695. }
  3696. }
  3697. ImportDescriptor += 1;
  3698. }
  3699. //
  3700. // All the imports are successfully loaded so establish and compact
  3701. // the import unload list.
  3702. //
  3703. if (ImportList) {
  3704. //
  3705. // Blank entries occur for things like the kernel, HAL & win32k.sys
  3706. // that we never want to unload. Especially for things like
  3707. // win32k.sys where the reference count can really hit 0.
  3708. //
  3709. //
  3710. // Initializing SingleEntry is not needed for correctness
  3711. // but without it the compiler cannot compile this code
  3712. // W4 to check for use of uninitialized variables.
  3713. //
  3714. SingleEntry = NULL;
  3715. Count = 0;
  3716. for (i = 0; i < ImportList->Count; i += 1) {
  3717. if (ImportList->Entry[i]) {
  3718. SingleEntry = POINTER_TO_SINGLE_ENTRY(ImportList->Entry[i]);
  3719. Count += 1;
  3720. }
  3721. }
  3722. if (Count == 0) {
  3723. ExFreePool(ImportList);
  3724. ImportList = NO_IMPORTS_USED;
  3725. }
  3726. else if (Count == 1) {
  3727. ExFreePool(ImportList);
  3728. ImportList = (PLOAD_IMPORTS)SingleEntry;
  3729. }
  3730. else if (Count != ImportList->Count) {
  3731. ImportListSize = Count * sizeof(PVOID) + sizeof(SIZE_T);
  3732. CompactedImportList = (PLOAD_IMPORTS)
  3733. ExAllocatePoolWithTag (PagedPool | POOL_COLD_ALLOCATION,
  3734. ImportListSize,
  3735. 'TDmM');
  3736. if (CompactedImportList) {
  3737. CompactedImportList->Count = Count;
  3738. Count = 0;
  3739. for (i = 0; i < ImportList->Count; i += 1) {
  3740. if (ImportList->Entry[i]) {
  3741. CompactedImportList->Entry[Count] = ImportList->Entry[i];
  3742. Count += 1;
  3743. }
  3744. }
  3745. ExFreePool(ImportList);
  3746. ImportList = CompactedImportList;
  3747. }
  3748. }
  3749. *LoadedImports = ImportList;
  3750. }
  3751. return STATUS_SUCCESS;
  3752. }
  3753. NTSTATUS
  3754. MiSnapThunk(
  3755. IN PVOID DllBase,
  3756. IN PVOID ImageBase,
  3757. IN PIMAGE_THUNK_DATA NameThunk,
  3758. OUT PIMAGE_THUNK_DATA AddrThunk,
  3759. IN PIMAGE_EXPORT_DIRECTORY ExportDirectory,
  3760. IN ULONG ExportSize,
  3761. IN LOGICAL SnapForwarder,
  3762. OUT PCHAR *MissingProcedureName
  3763. )
  3764. /*++
  3765. Routine Description:
  3766. This function snaps a thunk using the specified Export Section data.
  3767. If the section data does not support the thunk, then the thunk is
  3768. partially snapped (Dll field is still non-null, but snap address is
  3769. set).
  3770. Arguments:
  3771. DllBase - Base of DLL being snapped to.
  3772. ImageBase - Base of image that contains the thunks to snap.
  3773. Thunk - On input, supplies the thunk to snap. When successfully
  3774. snapped, the function field is set to point to the address in
  3775. the DLL, and the DLL field is set to NULL.
  3776. ExportDirectory - Supplies the Export Section data from a DLL.
  3777. SnapForwarder - Supplies TRUE if the snap is for a forwarder, and therefore
  3778. Address of Data is already setup.
  3779. Return Value:
  3780. STATUS_SUCCESS or STATUS_DRIVER_ENTRYPOINT_NOT_FOUND or
  3781. STATUS_DRIVER_ORDINAL_NOT_FOUND
  3782. --*/
  3783. {
  3784. BOOLEAN Ordinal;
  3785. USHORT OrdinalNumber;
  3786. PULONG NameTableBase;
  3787. PUSHORT NameOrdinalTableBase;
  3788. PULONG Addr;
  3789. USHORT HintIndex;
  3790. ULONG High;
  3791. ULONG Low;
  3792. ULONG Middle;
  3793. LONG Result;
  3794. NTSTATUS Status;
  3795. PCHAR MissingProcedureName2;
  3796. CHAR NameBuffer[ MAXIMUM_FILENAME_LENGTH ];
  3797. PAGED_CODE();
  3798. //
  3799. // Determine if snap is by name, or by ordinal
  3800. //
  3801. Ordinal = (BOOLEAN)IMAGE_SNAP_BY_ORDINAL(NameThunk->u1.Ordinal);
  3802. if (Ordinal && !SnapForwarder) {
  3803. OrdinalNumber = (USHORT)(IMAGE_ORDINAL(NameThunk->u1.Ordinal) -
  3804. ExportDirectory->Base);
  3805. *MissingProcedureName = (PCHAR)(ULONG_PTR)OrdinalNumber;
  3806. }
  3807. else {
  3808. //
  3809. // Change AddressOfData from an RVA to a VA.
  3810. //
  3811. if (!SnapForwarder) {
  3812. NameThunk->u1.AddressOfData = (ULONG_PTR)ImageBase + NameThunk->u1.AddressOfData;
  3813. }
  3814. strncpy (*MissingProcedureName,
  3815. (const PCHAR)&((PIMAGE_IMPORT_BY_NAME)NameThunk->u1.AddressOfData)->Name[0],
  3816. MAXIMUM_FILENAME_LENGTH - 1);
  3817. //
  3818. // Lookup Name in NameTable
  3819. //
  3820. NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
  3821. NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
  3822. //
  3823. // Before dropping into binary search, see if
  3824. // the hint index results in a successful
  3825. // match. If the hint index is zero, then
  3826. // drop into binary search.
  3827. //
  3828. HintIndex = ((PIMAGE_IMPORT_BY_NAME)NameThunk->u1.AddressOfData)->Hint;
  3829. if ((ULONG)HintIndex < ExportDirectory->NumberOfNames &&
  3830. !strcmp((PSZ)((PIMAGE_IMPORT_BY_NAME)NameThunk->u1.AddressOfData)->Name,
  3831. (PSZ)((PCHAR)DllBase + NameTableBase[HintIndex]))) {
  3832. OrdinalNumber = NameOrdinalTableBase[HintIndex];
  3833. }
  3834. else {
  3835. //
  3836. // Lookup the import name in the name table using a binary search.
  3837. //
  3838. Low = 0;
  3839. High = ExportDirectory->NumberOfNames - 1;
  3840. //
  3841. // Initializing Middle is not needed for correctness, but without it
  3842. // the compiler cannot compile this code W4 to check for use of
  3843. // uninitialized variables.
  3844. //
  3845. Middle = 0;
  3846. while (High >= Low) {
  3847. //
  3848. // Compute the next probe index and compare the import name
  3849. // with the export name entry.
  3850. //
  3851. Middle = (Low + High) >> 1;
  3852. Result = strcmp((const PCHAR)&((PIMAGE_IMPORT_BY_NAME)NameThunk->u1.AddressOfData)->Name[0],
  3853. (PCHAR)((PCHAR)DllBase + NameTableBase[Middle]));
  3854. if (Result < 0) {
  3855. High = Middle - 1;
  3856. } else if (Result > 0) {
  3857. Low = Middle + 1;
  3858. }
  3859. else {
  3860. break;
  3861. }
  3862. }
  3863. //
  3864. // If the high index is less than the low index, then a matching
  3865. // table entry was not found. Otherwise, get the ordinal number
  3866. // from the ordinal table.
  3867. //
  3868. if ((LONG)High < (LONG)Low) {
  3869. return STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
  3870. }
  3871. else {
  3872. OrdinalNumber = NameOrdinalTableBase[Middle];
  3873. }
  3874. }
  3875. }
  3876. //
  3877. // If OrdinalNumber is not within the Export Address Table,
  3878. // then DLL does not implement function. Snap to LDRP_BAD_DLL.
  3879. //
  3880. if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
  3881. Status = STATUS_DRIVER_ORDINAL_NOT_FOUND;
  3882. }
  3883. else {
  3884. MissingProcedureName2 = NameBuffer;
  3885. Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
  3886. *(PULONG_PTR)&AddrThunk->u1.Function = (ULONG_PTR)DllBase + Addr[OrdinalNumber];
  3887. // AddrThunk s/b used from here on.
  3888. Status = STATUS_SUCCESS;
  3889. if (((ULONG_PTR)AddrThunk->u1.Function > (ULONG_PTR)ExportDirectory) &&
  3890. ((ULONG_PTR)AddrThunk->u1.Function < ((ULONG_PTR)ExportDirectory + ExportSize)) ) {
  3891. UNICODE_STRING UnicodeString;
  3892. ANSI_STRING ForwardDllName;
  3893. PLIST_ENTRY NextEntry;
  3894. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  3895. ULONG LocalExportSize;
  3896. PIMAGE_EXPORT_DIRECTORY LocalExportDirectory;
  3897. Status = STATUS_DRIVER_ENTRYPOINT_NOT_FOUND;
  3898. //
  3899. // Include the dot in the length so we can do prefix later on.
  3900. //
  3901. ForwardDllName.Buffer = (PCHAR)AddrThunk->u1.Function;
  3902. ForwardDllName.Length = (USHORT)(strchr(ForwardDllName.Buffer, '.') -
  3903. ForwardDllName.Buffer + 1);
  3904. ForwardDllName.MaximumLength = ForwardDllName.Length;
  3905. if (NT_SUCCESS(RtlAnsiStringToUnicodeString(&UnicodeString,
  3906. &ForwardDllName,
  3907. TRUE))) {
  3908. NextEntry = PsLoadedModuleList.Flink;
  3909. while (NextEntry != &PsLoadedModuleList) {
  3910. DataTableEntry = CONTAINING_RECORD(NextEntry,
  3911. KLDR_DATA_TABLE_ENTRY,
  3912. InLoadOrderLinks);
  3913. //
  3914. // We have to do a case INSENSITIVE comparison for
  3915. // forwarder because the linker just took what is in the
  3916. // def file, as opposed to looking in the exporting
  3917. // image for the name.
  3918. // we also use the prefix function to ignore the .exe or
  3919. // .sys or .dll at the end.
  3920. //
  3921. if (RtlPrefixString((PSTRING)&UnicodeString,
  3922. (PSTRING)&DataTableEntry->BaseDllName,
  3923. TRUE)) {
  3924. LocalExportDirectory = (PIMAGE_EXPORT_DIRECTORY)
  3925. RtlImageDirectoryEntryToData(DataTableEntry->DllBase,
  3926. TRUE,
  3927. IMAGE_DIRECTORY_ENTRY_EXPORT,
  3928. &LocalExportSize);
  3929. if (LocalExportDirectory != NULL) {
  3930. IMAGE_THUNK_DATA thunkData;
  3931. PIMAGE_IMPORT_BY_NAME addressOfData;
  3932. SIZE_T length;
  3933. //
  3934. // One extra byte for NULL termination.
  3935. //
  3936. length = strlen(ForwardDllName.Buffer +
  3937. ForwardDllName.Length) + 1;
  3938. addressOfData = (PIMAGE_IMPORT_BY_NAME)
  3939. ExAllocatePoolWithTag (PagedPool,
  3940. length +
  3941. sizeof(IMAGE_IMPORT_BY_NAME),
  3942. ' mM');
  3943. if (addressOfData) {
  3944. RtlCopyMemory(&(addressOfData->Name[0]),
  3945. ForwardDllName.Buffer +
  3946. ForwardDllName.Length,
  3947. length);
  3948. addressOfData->Hint = 0;
  3949. *(PULONG_PTR)&thunkData.u1.AddressOfData =
  3950. (ULONG_PTR)addressOfData;
  3951. Status = MiSnapThunk(DataTableEntry->DllBase,
  3952. ImageBase,
  3953. &thunkData,
  3954. &thunkData,
  3955. LocalExportDirectory,
  3956. LocalExportSize,
  3957. TRUE,
  3958. &MissingProcedureName2);
  3959. ExFreePool(addressOfData);
  3960. AddrThunk->u1 = thunkData.u1;
  3961. }
  3962. }
  3963. break;
  3964. }
  3965. NextEntry = NextEntry->Flink;
  3966. }
  3967. RtlFreeUnicodeString(&UnicodeString);
  3968. }
  3969. }
  3970. }
  3971. return Status;
  3972. }
  3973. #if 0
  3974. PVOID
  3975. MiLookupImageSectionByName (
  3976. IN PVOID Base,
  3977. IN LOGICAL MappedAsImage,
  3978. IN PCHAR SectionName,
  3979. OUT PULONG SectionSize
  3980. )
  3981. /*++
  3982. Routine Description:
  3983. This function locates a Directory Entry within the image header
  3984. and returns either the virtual address or seek address of the
  3985. data the Directory describes.
  3986. Arguments:
  3987. Base - Supplies the base of the image or data file.
  3988. MappedAsImage - FALSE if the file is mapped as a data file.
  3989. - TRUE if the file is mapped as an image.
  3990. SectionName - Supplies the name of the section to lookup.
  3991. SectionSize - Return the size of the section.
  3992. Return Value:
  3993. NULL - The file does not contain data for the specified section.
  3994. NON-NULL - Returns the address where the section is mapped in memory.
  3995. --*/
  3996. {
  3997. ULONG i, j, Match;
  3998. PIMAGE_NT_HEADERS NtHeaders;
  3999. PIMAGE_SECTION_HEADER NtSection;
  4000. NtHeaders = RtlImageNtHeader(Base);
  4001. NtSection = IMAGE_FIRST_SECTION (NtHeaders);
  4002. for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++) {
  4003. Match = TRUE;
  4004. for (j = 0; j < IMAGE_SIZEOF_SHORT_NAME; j++) {
  4005. if (SectionName[j] != NtSection->Name[j]) {
  4006. Match = FALSE;
  4007. break;
  4008. }
  4009. if (SectionName[j] == '\0') {
  4010. break;
  4011. }
  4012. }
  4013. if (Match) {
  4014. break;
  4015. }
  4016. NtSection += 1;
  4017. }
  4018. if (Match) {
  4019. *SectionSize = NtSection->SizeOfRawData;
  4020. if (MappedAsImage) {
  4021. return (((PCHAR)Base + NtSection->VirtualAddress));
  4022. }
  4023. else {
  4024. return (((PCHAR)Base + NtSection->PointerToRawData));
  4025. }
  4026. }
  4027. return NULL;
  4028. }
  4029. #endif //0
  4030. NTSTATUS
  4031. MmCheckSystemImage (
  4032. IN HANDLE ImageFileHandle,
  4033. IN LOGICAL PurgeSection
  4034. )
  4035. /*++
  4036. Routine Description:
  4037. This function ensures the checksum for a system image is correct
  4038. and matches the data in the image.
  4039. Arguments:
  4040. ImageFileHandle - Supplies the file handle of the image.
  4041. PurgeSection - Supplies TRUE if the data section mapping the image should
  4042. be purged prior to returning. Note that the first page
  4043. could be used to speed up subsequent image section creation,
  4044. but generally the cost of useless data pages sitting in
  4045. transition is costly. Better to put the pages immediately
  4046. on the free list to preserve the transition cache for more
  4047. useful pages.
  4048. Return Value:
  4049. Status value.
  4050. --*/
  4051. {
  4052. NTSTATUS Status;
  4053. NTSTATUS Status2;
  4054. HANDLE Section;
  4055. PVOID ViewBase;
  4056. SIZE_T ViewSize;
  4057. IO_STATUS_BLOCK IoStatusBlock;
  4058. PIMAGE_FILE_HEADER FileHeader;
  4059. PIMAGE_NT_HEADERS NtHeaders;
  4060. FILE_STANDARD_INFORMATION StandardInfo;
  4061. PSECTION SectionPointer;
  4062. OBJECT_ATTRIBUTES ObjectAttributes;
  4063. KAPC_STATE ApcState;
  4064. PAGED_CODE();
  4065. InitializeObjectAttributes (&ObjectAttributes,
  4066. NULL,
  4067. (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
  4068. NULL,
  4069. NULL);
  4070. Status = ZwCreateSection (&Section,
  4071. SECTION_MAP_EXECUTE,
  4072. &ObjectAttributes,
  4073. NULL,
  4074. PAGE_EXECUTE,
  4075. SEC_COMMIT,
  4076. ImageFileHandle);
  4077. if (!NT_SUCCESS(Status)) {
  4078. return Status;
  4079. }
  4080. ViewBase = NULL;
  4081. ViewSize = 0;
  4082. //
  4083. // Since callees are not always in the context of the system process,
  4084. // attach here when necessary to guarantee the driver load occurs in a
  4085. // known safe address space to prevent security holes.
  4086. //
  4087. KeStackAttachProcess (&PsInitialSystemProcess->Pcb, &ApcState);
  4088. Status = ZwMapViewOfSection (Section,
  4089. NtCurrentProcess(),
  4090. (PVOID *)&ViewBase,
  4091. 0L,
  4092. 0L,
  4093. NULL,
  4094. &ViewSize,
  4095. ViewShare,
  4096. 0L,
  4097. PAGE_EXECUTE);
  4098. if (!NT_SUCCESS(Status)) {
  4099. KeUnstackDetachProcess (&ApcState);
  4100. ZwClose(Section);
  4101. return Status;
  4102. }
  4103. //
  4104. // Now the image is mapped as a data file... Calculate its size and then
  4105. // check its checksum.
  4106. //
  4107. Status = ZwQueryInformationFile (ImageFileHandle,
  4108. &IoStatusBlock,
  4109. &StandardInfo,
  4110. sizeof(StandardInfo),
  4111. FileStandardInformation);
  4112. if (NT_SUCCESS(Status)) {
  4113. try {
  4114. if (!LdrVerifyMappedImageMatchesChecksum (ViewBase, StandardInfo.EndOfFile.LowPart)) {
  4115. Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
  4116. goto out;
  4117. }
  4118. NtHeaders = RtlImageNtHeader (ViewBase);
  4119. if (NtHeaders == NULL) {
  4120. Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
  4121. goto out;
  4122. }
  4123. FileHeader = &NtHeaders->FileHeader;
  4124. //
  4125. // Detect configurations inadvertently trying to load 32-bit
  4126. // drivers on NT64 or mismatched platform architectures, etc.
  4127. //
  4128. if ((FileHeader->Machine != IMAGE_FILE_MACHINE_NATIVE) ||
  4129. (NtHeaders->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {
  4130. Status = STATUS_INVALID_IMAGE_PROTECT;
  4131. goto out;
  4132. }
  4133. #if !defined(NT_UP)
  4134. if (!MmVerifyImageIsOkForMpUse (ViewBase)) {
  4135. Status = STATUS_IMAGE_MP_UP_MISMATCH;
  4136. goto out;
  4137. }
  4138. #endif
  4139. } except (EXCEPTION_EXECUTE_HANDLER) {
  4140. Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
  4141. }
  4142. }
  4143. out:
  4144. ZwUnmapViewOfSection (NtCurrentProcess(), ViewBase);
  4145. KeUnstackDetachProcess (&ApcState);
  4146. if (PurgeSection == TRUE) {
  4147. Status2 = ObReferenceObjectByHandle (Section,
  4148. SECTION_MAP_EXECUTE,
  4149. MmSectionObjectType,
  4150. KernelMode,
  4151. (PVOID *) &SectionPointer,
  4152. (POBJECT_HANDLE_INFORMATION) NULL);
  4153. if (NT_SUCCESS (Status2)) {
  4154. MmPurgeSection (SectionPointer->Segment->ControlArea->FilePointer->SectionObjectPointer,
  4155. NULL,
  4156. 0,
  4157. FALSE);
  4158. ObDereferenceObject (SectionPointer);
  4159. }
  4160. }
  4161. ZwClose (Section);
  4162. return Status;
  4163. }
  4164. #if !defined(NT_UP)
  4165. BOOLEAN
  4166. MmVerifyImageIsOkForMpUse(
  4167. IN PVOID BaseAddress
  4168. )
  4169. {
  4170. PIMAGE_NT_HEADERS NtHeaders;
  4171. PAGED_CODE();
  4172. NtHeaders = RtlImageNtHeader(BaseAddress);
  4173. if (NtHeaders != NULL) {
  4174. if ((KeNumberProcessors > 1) &&
  4175. (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_UP_SYSTEM_ONLY)) {
  4176. return FALSE;
  4177. }
  4178. }
  4179. return TRUE;
  4180. }
  4181. #endif
  4182. PFN_NUMBER
  4183. MiDeleteSystemPagableVm (
  4184. IN PMMPTE PointerPte,
  4185. IN PFN_NUMBER NumberOfPtes,
  4186. IN MMPTE NewPteValue,
  4187. IN LOGICAL SessionAllocation,
  4188. OUT PPFN_NUMBER ResidentPages
  4189. )
  4190. /*++
  4191. Routine Description:
  4192. This function deletes pagable system address space (paged pool
  4193. or driver pagable sections).
  4194. Arguments:
  4195. PointerPte - Supplies the start of the PTE range to delete.
  4196. NumberOfPtes - Supplies the number of PTEs in the range.
  4197. NewPteValue - Supplies the new value for the PTE.
  4198. SessionAllocation - Supplies TRUE if this is a range in session space. If
  4199. TRUE is specified, it is assumed that the caller has
  4200. already attached to the relevant session.
  4201. If FALSE is supplied, then it is assumed that the range
  4202. is in the systemwide global space instead.
  4203. ResidentPages - If not NULL, the number of resident pages freed is
  4204. returned here.
  4205. Return Value:
  4206. Returns the number of pages actually freed.
  4207. --*/
  4208. {
  4209. PVOID VirtualAddress;
  4210. PFN_NUMBER PageFrameIndex;
  4211. MMPTE PteContents;
  4212. PMMPFN Pfn1;
  4213. PMMPFN Pfn2;
  4214. PFN_NUMBER ValidPages;
  4215. PFN_NUMBER PagesRequired;
  4216. MMPTE NewContents;
  4217. WSLE_NUMBER WsIndex;
  4218. KIRQL OldIrql;
  4219. KIRQL OldIrqlWs;
  4220. MMPTE_FLUSH_LIST PteFlushList;
  4221. MMPTE JunkPte;
  4222. MMWSLENTRY Locked;
  4223. PFN_NUMBER PageTableFrameIndex;
  4224. PETHREAD CurrentThread;
  4225. LOGICAL WsHeld;
  4226. ASSERT (KeGetCurrentIrql() <= APC_LEVEL);
  4227. ValidPages = 0;
  4228. PagesRequired = 0;
  4229. PteFlushList.Count = 0;
  4230. WsHeld = FALSE;
  4231. OldIrqlWs = PASSIVE_LEVEL;
  4232. NewContents = NewPteValue;
  4233. CurrentThread = PsGetCurrentThread ();
  4234. while (NumberOfPtes != 0) {
  4235. PteContents = *PointerPte;
  4236. if (PteContents.u.Long != ZeroKernelPte.u.Long) {
  4237. if (PteContents.u.Hard.Valid == 1) {
  4238. //
  4239. // Once the working set mutex is acquired, it is deliberately
  4240. // held until all the pages have been freed. This is because
  4241. // when paged pool is running low on large servers, we need the
  4242. // segment dereference thread to be able to free large amounts
  4243. // quickly. Typically this thread will free 64k chunks and we
  4244. // don't want to have to contend for the mutex 16 times to do
  4245. // this as there may be thousands of other threads also trying
  4246. // for it.
  4247. //
  4248. if (WsHeld == FALSE) {
  4249. WsHeld = TRUE;
  4250. if (SessionAllocation == TRUE) {
  4251. LOCK_SESSION_SPACE_WS (OldIrqlWs, CurrentThread);
  4252. }
  4253. else {
  4254. LOCK_SYSTEM_WS (OldIrqlWs, CurrentThread);
  4255. }
  4256. }
  4257. PteContents = *(volatile MMPTE *)PointerPte;
  4258. if (PteContents.u.Hard.Valid == 0) {
  4259. continue;
  4260. }
  4261. //
  4262. // Delete the page.
  4263. //
  4264. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (&PteContents);
  4265. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  4266. //
  4267. // Check to see if this is a pagable page in which
  4268. // case it needs to be removed from the working set list.
  4269. //
  4270. WsIndex = Pfn1->u1.WsIndex;
  4271. if (WsIndex == 0) {
  4272. ValidPages += 1;
  4273. if (SessionAllocation == TRUE) {
  4274. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_DELVA, 1);
  4275. MmSessionSpace->NonPagablePages -= 1;
  4276. }
  4277. }
  4278. else {
  4279. if (SessionAllocation == FALSE) {
  4280. MiRemoveWsle (WsIndex, MmSystemCacheWorkingSetList);
  4281. MiReleaseWsle (WsIndex, &MmSystemCacheWs);
  4282. }
  4283. else {
  4284. VirtualAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  4285. WsIndex = MiLocateWsle (VirtualAddress,
  4286. MmSessionSpace->Vm.VmWorkingSetList,
  4287. WsIndex);
  4288. ASSERT (WsIndex != WSLE_NULL_INDEX);
  4289. //
  4290. // Check to see if this entry is locked in
  4291. // the working set or locked in memory.
  4292. //
  4293. Locked = MmSessionSpace->Wsle[WsIndex].u1.e1;
  4294. MiRemoveWsle (WsIndex, MmSessionSpace->Vm.VmWorkingSetList);
  4295. MiReleaseWsle (WsIndex, &MmSessionSpace->Vm);
  4296. if (Locked.LockedInWs == 1 || Locked.LockedInMemory == 1) {
  4297. //
  4298. // This entry is locked.
  4299. //
  4300. MM_BUMP_SESS_COUNTER (MM_DBG_SESSION_NP_DELVA, 1);
  4301. MmSessionSpace->NonPagablePages -= 1;
  4302. ValidPages += 1;
  4303. ASSERT (WsIndex < MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic);
  4304. MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic -= 1;
  4305. if (WsIndex != MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic) {
  4306. WSLE_NUMBER Entry;
  4307. PVOID SwapVa;
  4308. Entry = MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic;
  4309. ASSERT (MmSessionSpace->Wsle[Entry].u1.e1.Valid);
  4310. SwapVa = MmSessionSpace->Wsle[Entry].u1.VirtualAddress;
  4311. SwapVa = PAGE_ALIGN (SwapVa);
  4312. MiSwapWslEntries (Entry,
  4313. WsIndex,
  4314. &MmSessionSpace->Vm);
  4315. }
  4316. }
  4317. else {
  4318. ASSERT (WsIndex >= MmSessionSpace->Vm.VmWorkingSetList->FirstDynamic);
  4319. }
  4320. }
  4321. }
  4322. LOCK_PFN (OldIrql);
  4323. #if DBG0
  4324. if ((Pfn1->u3.e2.ReferenceCount > 1) &&
  4325. (Pfn1->u3.e1.WriteInProgress == 0)) {
  4326. DbgPrint ("MM:SYSLOAD - deleting pool locked for I/O PTE %p, pfn %p, share=%x, refcount=%x, wsindex=%x\n",
  4327. PointerPte,
  4328. PageFrameIndex,
  4329. Pfn1->u2.ShareCount,
  4330. Pfn1->u3.e2.ReferenceCount,
  4331. Pfn1->u1.WsIndex);
  4332. //
  4333. // This case is valid only if the page being deleted
  4334. // contained a lookaside free list entry that wasn't mapped
  4335. // and multiple threads faulted on it and waited together.
  4336. // Some of the faulted threads are still on the ready
  4337. // list but haven't run yet, and so still have references
  4338. // to this page that they picked up during the fault.
  4339. // But this current thread has already allocated the
  4340. // lookaside entry and is now freeing the entire page.
  4341. //
  4342. // BUT - if it is NOT the above case, we really should
  4343. // trap here. However, we don't have a good way to
  4344. // distinguish between the two cases. Note
  4345. // that this complication was inserted when we went to
  4346. // cmpxchg8 because using locks would prevent anyone from
  4347. // accessing the lookaside freelist flinks like this.
  4348. //
  4349. // So, the ASSERT below comes out, but we leave the print
  4350. // above in (with more data added) for the case where it's
  4351. // not a lookaside contender with the reference count, but
  4352. // is instead a truly bad reference that needs to be
  4353. // debugged. The system should crash shortly thereafter
  4354. // and we'll at least have the above print to help us out.
  4355. //
  4356. // ASSERT (Pfn1->u3.e2.ReferenceCount == 1);
  4357. }
  4358. #endif //DBG
  4359. //
  4360. // Check if this is a prototype PTE.
  4361. //
  4362. if (Pfn1->u3.e1.PrototypePte == 1) {
  4363. PMMPTE PointerPde;
  4364. ASSERT (SessionAllocation == TRUE);
  4365. //
  4366. // Capture the state of the modified bit for this
  4367. // PTE.
  4368. //
  4369. MI_CAPTURE_DIRTY_BIT_TO_PFN (PointerPte, Pfn1);
  4370. //
  4371. // Decrement the share and valid counts of the page table
  4372. // page which maps this PTE.
  4373. //
  4374. PointerPde = MiGetPteAddress (PointerPte);
  4375. if (PointerPde->u.Hard.Valid == 0) {
  4376. #if (_MI_PAGING_LEVELS < 3)
  4377. if (!NT_SUCCESS(MiCheckPdeForPagedPool (PointerPte))) {
  4378. #endif
  4379. KeBugCheckEx (MEMORY_MANAGEMENT,
  4380. 0x61940,
  4381. (ULONG_PTR)PointerPte,
  4382. (ULONG_PTR)PointerPde->u.Long,
  4383. (ULONG_PTR)MiGetVirtualAddressMappedByPte(PointerPte));
  4384. #if (_MI_PAGING_LEVELS < 3)
  4385. }
  4386. #endif
  4387. }
  4388. PageTableFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPde);
  4389. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  4390. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  4391. //
  4392. // Decrement the share count for the physical page.
  4393. //
  4394. MiDecrementShareCount (PageFrameIndex);
  4395. //
  4396. // No need to worry about fork prototype PTEs
  4397. // for kernel addresses.
  4398. //
  4399. ASSERT (PointerPte > MiHighestUserPte);
  4400. }
  4401. else {
  4402. PageTableFrameIndex = Pfn1->u4.PteFrame;
  4403. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  4404. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  4405. MI_SET_PFN_DELETED (Pfn1);
  4406. MiDecrementShareCountOnly (PageFrameIndex);
  4407. }
  4408. MI_WRITE_INVALID_PTE (PointerPte, NewContents);
  4409. UNLOCK_PFN (OldIrql);
  4410. //
  4411. // Flush the TB for this page.
  4412. //
  4413. if (PteFlushList.Count != MM_MAXIMUM_FLUSH_COUNT) {
  4414. //
  4415. // We cannot rewrite the PTE later without creating a race
  4416. // condition. So point the FlushPte at a harmless
  4417. // location so MiFlushPteList does the right thing.
  4418. //
  4419. PteFlushList.FlushPte[PteFlushList.Count] =
  4420. (PMMPTE)&JunkPte;
  4421. PteFlushList.FlushVa[PteFlushList.Count] =
  4422. MiGetVirtualAddressMappedByPte (PointerPte);
  4423. PteFlushList.Count += 1;
  4424. }
  4425. } else if (PteContents.u.Soft.Prototype) {
  4426. ASSERT (SessionAllocation == TRUE);
  4427. //
  4428. // No need to worry about fork prototype PTEs
  4429. // for kernel addresses.
  4430. //
  4431. ASSERT (PointerPte >= MiHighestUserPte);
  4432. MI_WRITE_INVALID_PTE (PointerPte, NewContents);
  4433. //
  4434. // We currently commit for all prototype kernel mappings since
  4435. // we could copy-on-write.
  4436. //
  4437. } else if (PteContents.u.Soft.Transition == 1) {
  4438. LOCK_PFN (OldIrql);
  4439. PteContents = *(volatile MMPTE *)PointerPte;
  4440. if (PteContents.u.Soft.Transition == 0) {
  4441. UNLOCK_PFN (OldIrql);
  4442. continue;
  4443. }
  4444. //
  4445. // Transition, release page.
  4446. //
  4447. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_TRANSITION_PTE (&PteContents);
  4448. //
  4449. // Set the pointer to PTE as empty so the page
  4450. // is deleted when the reference count goes to zero.
  4451. //
  4452. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  4453. MI_SET_PFN_DELETED (Pfn1);
  4454. PageTableFrameIndex = Pfn1->u4.PteFrame;
  4455. Pfn2 = MI_PFN_ELEMENT (PageTableFrameIndex);
  4456. MiDecrementShareCountInline (Pfn2, PageTableFrameIndex);
  4457. //
  4458. // Check the reference count for the page, if the reference
  4459. // count is zero, move the page to the free list, if the
  4460. // reference count is not zero, ignore this page. When the
  4461. // reference count goes to zero, it will be placed on the
  4462. // free list.
  4463. //
  4464. if (Pfn1->u3.e2.ReferenceCount == 0) {
  4465. MiUnlinkPageFromList (Pfn1);
  4466. MiReleasePageFileSpace (Pfn1->OriginalPte);
  4467. MiInsertPageInFreeList (PageFrameIndex);
  4468. }
  4469. #if 0
  4470. //
  4471. // This assert is not valid since pool may now be the deferred
  4472. // MmUnlockPages queue in which case the reference count
  4473. // will be nonzero with no write in progress pending.
  4474. //
  4475. if ((Pfn1->u3.e2.ReferenceCount > 1) &&
  4476. (Pfn1->u3.e1.WriteInProgress == 0)) {
  4477. DbgPrint ("MM:SYSLOAD - deleting pool locked for I/O %p\n",
  4478. PageFrameIndex);
  4479. DbgBreakPoint();
  4480. }
  4481. #endif //DBG
  4482. MI_WRITE_INVALID_PTE (PointerPte, NewContents);
  4483. UNLOCK_PFN (OldIrql);
  4484. }
  4485. else {
  4486. //
  4487. // Demand zero, release page file space.
  4488. //
  4489. if (PteContents.u.Soft.PageFileHigh != 0) {
  4490. LOCK_PFN (OldIrql);
  4491. MiReleasePageFileSpace (PteContents);
  4492. UNLOCK_PFN (OldIrql);
  4493. }
  4494. MI_WRITE_INVALID_PTE (PointerPte, NewContents);
  4495. }
  4496. PagesRequired += 1;
  4497. }
  4498. NumberOfPtes -= 1;
  4499. PointerPte += 1;
  4500. }
  4501. if (WsHeld == TRUE) {
  4502. if (SessionAllocation == TRUE) {
  4503. UNLOCK_SESSION_SPACE_WS (OldIrqlWs);
  4504. }
  4505. else {
  4506. UNLOCK_SYSTEM_WS (OldIrqlWs);
  4507. }
  4508. }
  4509. //
  4510. // There is the thorny case where one of the pages we just deleted could
  4511. // get faulted back in when we released the PFN lock above within the loop.
  4512. // The only time when this can happen is if a thread faulted during an
  4513. // interlocked pool allocation for an address that we've just deleted.
  4514. //
  4515. // If this thread sees the NewContents in the PTE, it will treat it as
  4516. // demand zero, and incorrectly allocate a page, PTE and WSL. It will
  4517. // reference it once and realize it needs to reread the lookaside listhead
  4518. // and restart the operation. But this page would live on in paged pool
  4519. // as modified (but unused) until the paged pool allocator chose to give
  4520. // out its virtual address again.
  4521. //
  4522. // The code below rewrites the PTEs which is really bad if another thread
  4523. // gets a zero page between our first setting of the PTEs above and our
  4524. // second setting below - because we'll reset the PTE to demand zero, but
  4525. // we'll still have a WSL entry that's valid, and we spiral from there.
  4526. //
  4527. // We really should remove the writing of the PTE below since we've already
  4528. // done it above. But for now, we're leaving it in - it's harmless because
  4529. // we've chosen to fix this problem by checking for this case when we
  4530. // materialize demand zero pages. Note that we have to fix this problem
  4531. // by checking in the demand zero path because a thread could be coming into
  4532. // that path any time before or after we flush the PTE list and any fixes
  4533. // here could only address the before case, not the after.
  4534. //
  4535. if (SessionAllocation == TRUE) {
  4536. //
  4537. // Session space has no ASN - flush the entire TB.
  4538. //
  4539. MI_FLUSH_ENTIRE_SESSION_TB (TRUE, TRUE);
  4540. }
  4541. MiFlushPteList (&PteFlushList, TRUE, NewContents);
  4542. if (ARGUMENT_PRESENT(ResidentPages)) {
  4543. *ResidentPages = ValidPages;
  4544. }
  4545. return PagesRequired;
  4546. }
  4547. VOID
  4548. MiSetImageProtect (
  4549. IN PSEGMENT Segment,
  4550. IN ULONG Protection
  4551. )
  4552. /*++
  4553. Routine Description:
  4554. This function sets the protection of all prototype PTEs to the specified
  4555. protection.
  4556. Arguments:
  4557. Segment - Supplies a pointer to the segment to protect.
  4558. Protection - Supplies the protection value to set.
  4559. Return Value:
  4560. None.
  4561. --*/
  4562. {
  4563. PMMPTE PointerPte;
  4564. PMMPTE StartPte;
  4565. PMMPTE LastPte;
  4566. MMPTE PteContents;
  4567. PSUBSECTION Subsection;
  4568. //
  4569. // Set the subsection protections.
  4570. //
  4571. ASSERT (Segment->ControlArea->u.Flags.GlobalOnlyPerSession == 0);
  4572. if ((Segment->ControlArea->u.Flags.GlobalOnlyPerSession == 0) &&
  4573. (Segment->ControlArea->u.Flags.Rom == 0)) {
  4574. Subsection = (PSUBSECTION)(Segment->ControlArea + 1);
  4575. }
  4576. else {
  4577. Subsection = (PSUBSECTION)((PLARGE_CONTROL_AREA)(Segment->ControlArea) + 1);
  4578. }
  4579. if ((Protection & MM_PROTECTION_WRITE_MASK) == 0) {
  4580. Subsection->u.SubsectionFlags.Protection = Protection;
  4581. Subsection->u.SubsectionFlags.ReadOnly = 1;
  4582. }
  4583. StartPte = Subsection->SubsectionBase;
  4584. PointerPte = StartPte;
  4585. LastPte = PointerPte + Segment->NonExtendedPtes;
  4586. MmLockPagedPool (PointerPte, (LastPte - PointerPte) * sizeof (MMPTE));
  4587. do {
  4588. PteContents = *PointerPte;
  4589. ASSERT (PteContents.u.Hard.Valid == 0);
  4590. if (PteContents.u.Long != ZeroPte.u.Long) {
  4591. if ((PteContents.u.Soft.Prototype == 0) &&
  4592. (PteContents.u.Soft.Transition == 1)) {
  4593. if (MiSetProtectionOnTransitionPte (PointerPte, Protection)) {
  4594. continue;
  4595. }
  4596. }
  4597. else {
  4598. PointerPte->u.Soft.Protection = Protection;
  4599. }
  4600. }
  4601. PointerPte += 1;
  4602. } while (PointerPte < LastPte);
  4603. MmUnlockPagedPool (StartPte, (LastPte - StartPte) * sizeof (MMPTE));
  4604. return;
  4605. }
  4606. VOID
  4607. MiSetSystemCodeProtection (
  4608. IN PMMPTE FirstPte,
  4609. IN PMMPTE LastPte
  4610. )
  4611. /*++
  4612. Routine Description:
  4613. This function sets the protection of system code to read only.
  4614. Note this is different from protecting section-backed code like win32k.
  4615. Arguments:
  4616. FirstPte - Supplies the starting PTE.
  4617. LastPte - Supplies the ending PTE.
  4618. Return Value:
  4619. None.
  4620. Environment:
  4621. Kernel Mode, IRQL of APC_LEVEL or below.
  4622. This routine could be made PAGELK but it is a high frequency routine
  4623. so it is actually better to keep it nonpaged to avoid bringing in the
  4624. entire PAGELK section.
  4625. --*/
  4626. {
  4627. KIRQL OldIrql;
  4628. MMPTE PteContents;
  4629. MMPTE TempPte;
  4630. MMPTE PreviousPte;
  4631. PMMPTE PointerPte;
  4632. PMMPTE PointerPde;
  4633. PMMPTE PointerProtoPte;
  4634. ULONG ProtectionMask;
  4635. PMMPFN Pfn1;
  4636. PMMPFN ProtoPfn;
  4637. LOGICAL SessionAddress;
  4638. #if defined(_X86_)
  4639. ASSERT (MI_IS_PHYSICAL_ADDRESS(MiGetVirtualAddressMappedByPte(FirstPte)) == 0);
  4640. #endif
  4641. SessionAddress = FALSE;
  4642. if (MI_IS_SESSION_ADDRESS(MiGetVirtualAddressMappedByPte(FirstPte))) {
  4643. SessionAddress = TRUE;
  4644. }
  4645. ProtectionMask = MM_EXECUTE_READ;
  4646. //
  4647. // Make these PTEs read only.
  4648. //
  4649. // Note that the write bit may already be off (in the valid PTE) if the
  4650. // page has already been inpaged from the paging file and has not since
  4651. // been dirtied.
  4652. //
  4653. PointerPte = FirstPte;
  4654. LOCK_PFN (OldIrql);
  4655. while (PointerPte <= LastPte) {
  4656. PteContents = *PointerPte;
  4657. if ((PteContents.u.Long == 0) || (!*MiPteStr)) {
  4658. PointerPte += 1;
  4659. continue;
  4660. }
  4661. if (PteContents.u.Hard.Valid == 1) {
  4662. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Hard.PageFrameNumber);
  4663. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  4664. //
  4665. // Note the dirty and write bits get turned off here.
  4666. // Any existing pagefile addresses for clean pages are preserved.
  4667. //
  4668. if (MI_IS_PTE_DIRTY(PteContents)) {
  4669. MI_CAPTURE_DIRTY_BIT_TO_PFN (&PteContents, Pfn1);
  4670. }
  4671. MI_MAKE_VALID_PTE (TempPte,
  4672. PteContents.u.Hard.PageFrameNumber,
  4673. Pfn1->OriginalPte.u.Soft.Protection,
  4674. PointerPte);
  4675. if (SessionAddress == TRUE) {
  4676. //
  4677. // Session space has no ASN - flush the entire TB.
  4678. //
  4679. MI_FLUSH_SINGLE_SESSION_TB (MiGetVirtualAddressMappedByPte (PointerPte),
  4680. TRUE,
  4681. TRUE,
  4682. (PHARDWARE_PTE)PointerPte,
  4683. TempPte.u.Flush,
  4684. PreviousPte);
  4685. }
  4686. else {
  4687. KeFlushSingleTb (MiGetVirtualAddressMappedByPte (PointerPte),
  4688. TRUE,
  4689. TRUE,
  4690. (PHARDWARE_PTE)PointerPte,
  4691. TempPte.u.Flush);
  4692. }
  4693. }
  4694. else if (PteContents.u.Soft.Prototype == 1) {
  4695. if (SessionAddress == TRUE) {
  4696. PointerPte->u.Proto.ReadOnly = 1;
  4697. }
  4698. else {
  4699. PointerProtoPte = MiPteToProto(PointerPte);
  4700. ASSERT (!MI_IS_PHYSICAL_ADDRESS(PointerProtoPte));
  4701. PointerPde = MiGetPteAddress (PointerProtoPte);
  4702. if (PointerPde->u.Hard.Valid == 0) {
  4703. MiMakeSystemAddressValidPfn (PointerProtoPte);
  4704. //
  4705. // The world may change if we had to wait.
  4706. //
  4707. PteContents = *PointerPte;
  4708. if ((PteContents.u.Hard.Valid == 1) ||
  4709. (PteContents.u.Soft.Prototype == 0)) {
  4710. continue;
  4711. }
  4712. }
  4713. ProtoPfn = MI_PFN_ELEMENT (PointerPde->u.Hard.PageFrameNumber);
  4714. MI_ADD_LOCKED_PAGE_CHARGE(ProtoPfn, 12);
  4715. ProtoPfn->u3.e2.ReferenceCount += 1;
  4716. ASSERT (ProtoPfn->u3.e2.ReferenceCount > 1);
  4717. PteContents = *PointerProtoPte;
  4718. if (PteContents.u.Long != ZeroPte.u.Long) {
  4719. ASSERT (PteContents.u.Hard.Valid == 0);
  4720. PointerProtoPte->u.Soft.Protection = ProtectionMask;
  4721. if ((PteContents.u.Soft.Prototype == 0) &&
  4722. (PteContents.u.Soft.Transition == 1)) {
  4723. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  4724. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  4725. }
  4726. }
  4727. ASSERT (ProtoPfn->u3.e2.ReferenceCount > 1);
  4728. MI_REMOVE_LOCKED_PAGE_CHARGE_AND_DECREF(ProtoPfn, 13);
  4729. }
  4730. }
  4731. else if (PteContents.u.Soft.Transition == 1) {
  4732. Pfn1 = MI_PFN_ELEMENT (PteContents.u.Trans.PageFrameNumber);
  4733. Pfn1->OriginalPte.u.Soft.Protection = ProtectionMask;
  4734. PointerPte->u.Soft.Protection = ProtectionMask;
  4735. }
  4736. else {
  4737. //
  4738. // Must be page file space or demand zero.
  4739. //
  4740. PointerPte->u.Soft.Protection = ProtectionMask;
  4741. }
  4742. PointerPte += 1;
  4743. }
  4744. UNLOCK_PFN (OldIrql);
  4745. }
  4746. VOID
  4747. MiWriteProtectSystemImage (
  4748. IN PVOID DllBase
  4749. )
  4750. /*++
  4751. Routine Description:
  4752. This function sets the protection of a system component to read only.
  4753. Arguments:
  4754. DllBase - Supplies the base address of the system component.
  4755. Return Value:
  4756. None.
  4757. --*/
  4758. {
  4759. ULONG SectionProtection;
  4760. ULONG NumberOfSubsections;
  4761. ULONG SectionVirtualSize;
  4762. ULONG ImageAlignment;
  4763. ULONG OffsetToSectionTable;
  4764. ULONG NumberOfPtes;
  4765. ULONG_PTR VirtualAddress;
  4766. PVOID LastVirtualAddress;
  4767. PMMPTE PointerPte;
  4768. PMMPTE FirstPte;
  4769. PMMPTE LastPte;
  4770. PMMPTE LastImagePte;
  4771. PMMPTE WritablePte;
  4772. PIMAGE_NT_HEADERS NtHeader;
  4773. PIMAGE_FILE_HEADER FileHeader;
  4774. PIMAGE_SECTION_HEADER SectionTableEntry;
  4775. PAGED_CODE();
  4776. if (MI_IS_PHYSICAL_ADDRESS(DllBase)) {
  4777. return;
  4778. }
  4779. NtHeader = RtlImageNtHeader (DllBase);
  4780. ASSERT (NtHeader);
  4781. ImageAlignment = NtHeader->OptionalHeader.SectionAlignment;
  4782. //
  4783. // All session drivers must be one way or the other - no mixing is allowed
  4784. // within multiple copy-on-write drivers.
  4785. //
  4786. if (MI_IS_SESSION_ADDRESS(DllBase) == 0) {
  4787. //
  4788. // Images prior to NT5 were not protected from stepping all over
  4789. // their (and others) code and readonly data. Here we somewhat
  4790. // preserve that behavior, but don't allow them to step on anyone else.
  4791. //
  4792. if (NtHeader->OptionalHeader.MajorOperatingSystemVersion < 5) {
  4793. return;
  4794. }
  4795. if (NtHeader->OptionalHeader.MajorImageVersion < 5) {
  4796. return;
  4797. }
  4798. }
  4799. NumberOfPtes = BYTES_TO_PAGES (NtHeader->OptionalHeader.SizeOfImage);
  4800. FileHeader = &NtHeader->FileHeader;
  4801. NumberOfSubsections = FileHeader->NumberOfSections;
  4802. ASSERT (NumberOfSubsections != 0);
  4803. OffsetToSectionTable = sizeof(ULONG) +
  4804. sizeof(IMAGE_FILE_HEADER) +
  4805. FileHeader->SizeOfOptionalHeader;
  4806. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  4807. OffsetToSectionTable);
  4808. //
  4809. // Verify the image contains subsections ordered by increasing virtual
  4810. // address and that there are no overlaps.
  4811. //
  4812. FirstPte = NULL;
  4813. LastVirtualAddress = DllBase;
  4814. for ( ; NumberOfSubsections > 0; NumberOfSubsections -= 1, SectionTableEntry += 1) {
  4815. if (SectionTableEntry->Misc.VirtualSize == 0) {
  4816. SectionVirtualSize = SectionTableEntry->SizeOfRawData;
  4817. }
  4818. else {
  4819. SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
  4820. }
  4821. VirtualAddress = (ULONG_PTR)DllBase + SectionTableEntry->VirtualAddress;
  4822. if ((PVOID)VirtualAddress <= LastVirtualAddress) {
  4823. //
  4824. // Subsections are not in an increasing virtual address ordering.
  4825. // No protection is provided for such a poorly linked image.
  4826. //
  4827. KdPrint (("MM:sysload - Image at %p is badly linked\n", DllBase));
  4828. return;
  4829. }
  4830. LastVirtualAddress = (PVOID)((PCHAR)VirtualAddress + SectionVirtualSize - 1);
  4831. }
  4832. NumberOfSubsections = FileHeader->NumberOfSections;
  4833. ASSERT (NumberOfSubsections != 0);
  4834. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  4835. OffsetToSectionTable);
  4836. LastVirtualAddress = NULL;
  4837. //
  4838. // Set writable PTE here so the image headers are excluded. This is
  4839. // needed so that locking down of sections can continue to edit the
  4840. // image headers for counts.
  4841. //
  4842. WritablePte = MiGetPteAddress ((PVOID)((ULONG_PTR)(SectionTableEntry + NumberOfSubsections) - 1));
  4843. LastImagePte = MiGetPteAddress(DllBase) + NumberOfPtes;
  4844. for ( ; NumberOfSubsections > 0; NumberOfSubsections -= 1, SectionTableEntry += 1) {
  4845. if (SectionTableEntry->Misc.VirtualSize == 0) {
  4846. SectionVirtualSize = SectionTableEntry->SizeOfRawData;
  4847. }
  4848. else {
  4849. SectionVirtualSize = SectionTableEntry->Misc.VirtualSize;
  4850. }
  4851. VirtualAddress = (ULONG_PTR)DllBase + SectionTableEntry->VirtualAddress;
  4852. PointerPte = MiGetPteAddress ((PVOID)VirtualAddress);
  4853. if (PointerPte >= LastImagePte) {
  4854. //
  4855. // Skip relocation subsections (which aren't given VA space).
  4856. //
  4857. break;
  4858. }
  4859. SectionProtection = (SectionTableEntry->Characteristics & (IMAGE_SCN_MEM_WRITE | IMAGE_SCN_MEM_READ | IMAGE_SCN_MEM_EXECUTE));
  4860. if (SectionProtection & IMAGE_SCN_MEM_WRITE) {
  4861. //
  4862. // This is a writable subsection, skip it. Make sure if it's
  4863. // sharing a PTE (and update the linker so this doesn't happen
  4864. // for the kernel at least) that the last PTE isn't made
  4865. // read only.
  4866. //
  4867. WritablePte = MiGetPteAddress ((PVOID)(VirtualAddress + SectionVirtualSize - 1));
  4868. if (LastVirtualAddress != NULL) {
  4869. LastPte = (PVOID) MiGetPteAddress (LastVirtualAddress);
  4870. if (LastPte == PointerPte) {
  4871. LastPte -= 1;
  4872. }
  4873. if (FirstPte <= LastPte) {
  4874. ASSERT (PointerPte < LastImagePte);
  4875. if (LastPte >= LastImagePte) {
  4876. LastPte = LastImagePte - 1;
  4877. }
  4878. MiSetSystemCodeProtection (FirstPte, LastPte);
  4879. }
  4880. LastVirtualAddress = NULL;
  4881. }
  4882. continue;
  4883. }
  4884. if (LastVirtualAddress == NULL) {
  4885. //
  4886. // There is no previous subsection or the previous
  4887. // subsection was writable. Thus the current starting PTE
  4888. // could be mapping both a readonly and a readwrite
  4889. // subsection if the image alignment is less than PAGE_SIZE.
  4890. // These cases (in either order) are handled here.
  4891. //
  4892. if (PointerPte == WritablePte) {
  4893. LastPte = MiGetPteAddress ((PVOID)(VirtualAddress + SectionVirtualSize - 1));
  4894. if (PointerPte == LastPte) {
  4895. //
  4896. // Nothing can be protected in this subsection
  4897. // due to the image alignment specified for the executable.
  4898. //
  4899. continue;
  4900. }
  4901. PointerPte += 1;
  4902. }
  4903. FirstPte = PointerPte;
  4904. }
  4905. LastVirtualAddress = (PVOID)((PCHAR)VirtualAddress + SectionVirtualSize - 1);
  4906. }
  4907. if (LastVirtualAddress != NULL) {
  4908. LastPte = (PVOID) MiGetPteAddress (LastVirtualAddress);
  4909. if ((FirstPte <= LastPte) && (FirstPte < LastImagePte)) {
  4910. if (LastPte >= LastImagePte) {
  4911. LastPte = LastImagePte - 1;
  4912. }
  4913. MiSetSystemCodeProtection (FirstPte, LastPte);
  4914. }
  4915. }
  4916. }
  4917. VOID
  4918. MiUpdateThunks (
  4919. IN PLOADER_PARAMETER_BLOCK LoaderBlock,
  4920. IN PVOID OldAddress,
  4921. IN PVOID NewAddress,
  4922. IN ULONG NumberOfBytes
  4923. )
  4924. /*++
  4925. Routine Description:
  4926. This function updates the IATs of all the loaded modules in the system
  4927. to handle a newly relocated image.
  4928. Arguments:
  4929. LoaderBlock - Supplies a pointer to the system loader block.
  4930. OldAddress - Supplies the old address of the DLL which was just relocated.
  4931. NewAddress - Supplies the new address of the DLL which was just relocated.
  4932. NumberOfBytes - Supplies the number of bytes spanned by the DLL.
  4933. Return Value:
  4934. None.
  4935. --*/
  4936. {
  4937. PULONG_PTR ImportThunk;
  4938. ULONG_PTR OldAddressHigh;
  4939. ULONG_PTR AddressDifference;
  4940. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  4941. PLIST_ENTRY NextEntry;
  4942. ULONG_PTR i;
  4943. ULONG ImportSize;
  4944. //
  4945. // Note this routine must not call any modules outside the kernel.
  4946. // This is because that module may itself be the one being relocated right
  4947. // now.
  4948. //
  4949. OldAddressHigh = (ULONG_PTR)((PCHAR)OldAddress + NumberOfBytes - 1);
  4950. AddressDifference = (ULONG_PTR)NewAddress - (ULONG_PTR)OldAddress;
  4951. NextEntry = LoaderBlock->LoadOrderListHead.Flink;
  4952. for ( ; NextEntry != &LoaderBlock->LoadOrderListHead; NextEntry = NextEntry->Flink) {
  4953. DataTableEntry = CONTAINING_RECORD(NextEntry,
  4954. KLDR_DATA_TABLE_ENTRY,
  4955. InLoadOrderLinks);
  4956. ImportThunk = (PULONG_PTR)RtlImageDirectoryEntryToData(
  4957. DataTableEntry->DllBase,
  4958. TRUE,
  4959. IMAGE_DIRECTORY_ENTRY_IAT,
  4960. &ImportSize);
  4961. if (ImportThunk == NULL) {
  4962. continue;
  4963. }
  4964. ImportSize /= sizeof(PULONG_PTR);
  4965. for (i = 0; i < ImportSize; i += 1, ImportThunk += 1) {
  4966. if (*ImportThunk >= (ULONG_PTR)OldAddress && *ImportThunk <= OldAddressHigh) {
  4967. *ImportThunk += AddressDifference;
  4968. }
  4969. }
  4970. }
  4971. }
  4972. VOID
  4973. MiReloadBootLoadedDrivers (
  4974. IN PLOADER_PARAMETER_BLOCK LoaderBlock
  4975. )
  4976. /*++
  4977. Routine Description:
  4978. The kernel, HAL and boot drivers are relocated by the loader.
  4979. All the boot drivers are then relocated again here.
  4980. This function relocates osloader-loaded images into system PTEs. This
  4981. gives these images the benefits that all other drivers already enjoy,
  4982. including :
  4983. 1. Paging of the drivers (this is more than 500K today).
  4984. 2. Write-protection of their text sections.
  4985. 3. Automatic unload of drivers on last dereference.
  4986. Note care must be taken when processing HIGHADJ relocations more than once.
  4987. Arguments:
  4988. LoaderBlock - Supplies a pointer to the system loader block.
  4989. Return Value:
  4990. None.
  4991. Environment:
  4992. Kernel mode, Phase 0 Initialization.
  4993. --*/
  4994. {
  4995. LOGICAL HasRelocations;
  4996. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  4997. PLIST_ENTRY NextEntry;
  4998. PIMAGE_FILE_HEADER FileHeader;
  4999. PIMAGE_NT_HEADERS NtHeader;
  5000. PIMAGE_DATA_DIRECTORY DataDirectory;
  5001. ULONG_PTR i;
  5002. ULONG NumberOfPtes;
  5003. ULONG NumberOfLoaderPtes;
  5004. PMMPTE PointerPte;
  5005. PMMPTE LastPte;
  5006. PMMPTE LoaderPte;
  5007. MMPTE PteContents;
  5008. MMPTE TempPte;
  5009. PVOID LoaderImageAddress;
  5010. PVOID NewImageAddress;
  5011. NTSTATUS Status;
  5012. PFN_NUMBER PageFrameIndex;
  5013. PFN_NUMBER PteFramePage;
  5014. PMMPTE PteFramePointer;
  5015. PMMPFN Pfn1;
  5016. PMMPFN Pfn2;
  5017. KIRQL OldIrql;
  5018. PCHAR RelocatedVa;
  5019. PCHAR NonRelocatedVa;
  5020. LOGICAL StopMoving;
  5021. #if !defined (_X86_)
  5022. //
  5023. // Only try to preserve low memory on x86 machines.
  5024. //
  5025. MmMakeLowMemory = FALSE;
  5026. #endif
  5027. StopMoving = FALSE;
  5028. i = 0;
  5029. NextEntry = LoaderBlock->LoadOrderListHead.Flink;
  5030. for ( ; NextEntry != &LoaderBlock->LoadOrderListHead; NextEntry = NextEntry->Flink) {
  5031. //
  5032. // Skip the kernel and the HAL. Note their relocation sections will
  5033. // be automatically reclaimed.
  5034. //
  5035. i += 1;
  5036. if (i <= 2) {
  5037. continue;
  5038. }
  5039. DataTableEntry = CONTAINING_RECORD(NextEntry,
  5040. KLDR_DATA_TABLE_ENTRY,
  5041. InLoadOrderLinks);
  5042. NtHeader = RtlImageNtHeader (DataTableEntry->DllBase);
  5043. //
  5044. // Ensure that the relocation section exists and that the loader
  5045. // hasn't freed it already.
  5046. //
  5047. if (NtHeader == NULL) {
  5048. continue;
  5049. }
  5050. FileHeader = &NtHeader->FileHeader;
  5051. if (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) {
  5052. continue;
  5053. }
  5054. if (IMAGE_DIRECTORY_ENTRY_BASERELOC >= NtHeader->OptionalHeader.NumberOfRvaAndSizes) {
  5055. continue;
  5056. }
  5057. DataDirectory = &NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
  5058. if (DataDirectory->VirtualAddress == 0) {
  5059. HasRelocations = FALSE;
  5060. }
  5061. else {
  5062. if (DataDirectory->VirtualAddress + DataDirectory->Size > DataTableEntry->SizeOfImage) {
  5063. //
  5064. // The relocation section has already been freed, the user must
  5065. // be using an old loader that didn't save the relocations.
  5066. //
  5067. continue;
  5068. }
  5069. HasRelocations = TRUE;
  5070. }
  5071. LoaderImageAddress = DataTableEntry->DllBase;
  5072. LoaderPte = MiGetPteAddress(DataTableEntry->DllBase);
  5073. NumberOfLoaderPtes = (ULONG)((ROUND_TO_PAGES(DataTableEntry->SizeOfImage)) >> PAGE_SHIFT);
  5074. LOCK_PFN (OldIrql);
  5075. PointerPte = LoaderPte;
  5076. LastPte = PointerPte + NumberOfLoaderPtes;
  5077. while (PointerPte < LastPte) {
  5078. ASSERT (PointerPte->u.Hard.Valid == 1);
  5079. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  5080. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5081. //
  5082. // Mark the page as modified so boot drivers that call
  5083. // MmPageEntireDriver don't lose their unmodified data !
  5084. //
  5085. MI_SET_MODIFIED (Pfn1, 1, 0x14);
  5086. PointerPte += 1;
  5087. }
  5088. UNLOCK_PFN (OldIrql);
  5089. //
  5090. // Extra PTEs are allocated here to map the relocation section at the
  5091. // new address so the image can be relocated.
  5092. //
  5093. NumberOfPtes = NumberOfLoaderPtes;
  5094. PointerPte = MiReserveSystemPtes (NumberOfPtes, SystemPteSpace);
  5095. if (PointerPte == NULL) {
  5096. continue;
  5097. }
  5098. LastPte = PointerPte + NumberOfPtes;
  5099. NewImageAddress = MiGetVirtualAddressMappedByPte (PointerPte);
  5100. #if DBG_SYSLOAD
  5101. DbgPrint ("Relocating %wZ from %p to %p, %x bytes\n",
  5102. &DataTableEntry->FullDllName,
  5103. DataTableEntry->DllBase,
  5104. NewImageAddress,
  5105. DataTableEntry->SizeOfImage
  5106. );
  5107. #endif
  5108. //
  5109. // This assert is important because the assumption is made that PTEs
  5110. // (not superpages) are mapping these drivers.
  5111. //
  5112. ASSERT (InitializationPhase == 0);
  5113. //
  5114. // If the system is configured to make low memory available for ISA
  5115. // type drivers, then copy the boot loaded drivers now. Otherwise
  5116. // only PTE adjustment is done. Presumably some day when ISA goes
  5117. // away this code can be removed.
  5118. //
  5119. RelocatedVa = NewImageAddress;
  5120. NonRelocatedVa = (PCHAR) DataTableEntry->DllBase;
  5121. while (PointerPte < LastPte) {
  5122. PteContents = *LoaderPte;
  5123. ASSERT (PteContents.u.Hard.Valid == 1);
  5124. if (MmMakeLowMemory == TRUE) {
  5125. #if DBG
  5126. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (LoaderPte);
  5127. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5128. ASSERT (Pfn1->u1.WsIndex == 0);
  5129. #endif
  5130. LOCK_PFN (OldIrql);
  5131. MiEnsureAvailablePageOrWait (NULL, NULL);
  5132. PageFrameIndex = MiRemoveAnyPage(
  5133. MI_GET_PAGE_COLOR_FROM_PTE (PointerPte));
  5134. if (PageFrameIndex < (16*1024*1024)/PAGE_SIZE) {
  5135. //
  5136. // If the frames cannot be replaced with high pages
  5137. // then stop copying.
  5138. //
  5139. #if defined (_MI_MORE_THAN_4GB_)
  5140. if (MiNoLowMemory == 0)
  5141. #endif
  5142. StopMoving = TRUE;
  5143. }
  5144. MI_MAKE_VALID_PTE (TempPte,
  5145. PageFrameIndex,
  5146. MM_EXECUTE_READWRITE,
  5147. PointerPte);
  5148. MI_SET_PTE_DIRTY (TempPte);
  5149. MI_SET_ACCESSED_IN_PTE (&TempPte, 1);
  5150. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  5151. MiInitializePfn (PageFrameIndex, PointerPte, 1);
  5152. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5153. MI_SET_MODIFIED (Pfn1, 1, 0x15);
  5154. //
  5155. // Initialize the WsIndex just like the original page had it.
  5156. //
  5157. Pfn1->u1.WsIndex = 0;
  5158. UNLOCK_PFN (OldIrql);
  5159. RtlCopyMemory (RelocatedVa, NonRelocatedVa, PAGE_SIZE);
  5160. RelocatedVa += PAGE_SIZE;
  5161. NonRelocatedVa += PAGE_SIZE;
  5162. }
  5163. else {
  5164. MI_MAKE_VALID_PTE (TempPte,
  5165. PteContents.u.Hard.PageFrameNumber,
  5166. MM_EXECUTE_READWRITE,
  5167. PointerPte);
  5168. MI_SET_PTE_DIRTY (TempPte);
  5169. MI_WRITE_VALID_PTE (PointerPte, TempPte);
  5170. }
  5171. PointerPte += 1;
  5172. LoaderPte += 1;
  5173. }
  5174. PointerPte -= NumberOfPtes;
  5175. ASSERT (*(PULONG)NewImageAddress == *(PULONG)LoaderImageAddress);
  5176. //
  5177. // Image is now mapped at the new address. Relocate it (again).
  5178. //
  5179. NtHeader->OptionalHeader.ImageBase = (ULONG_PTR)LoaderImageAddress;
  5180. if (MmMakeLowMemory == TRUE) {
  5181. PIMAGE_NT_HEADERS NtHeader2;
  5182. NtHeader2 = (PIMAGE_NT_HEADERS)((PCHAR)NtHeader + (RelocatedVa - NonRelocatedVa));
  5183. NtHeader2->OptionalHeader.ImageBase = (ULONG_PTR)LoaderImageAddress;
  5184. }
  5185. if (HasRelocations == TRUE) {
  5186. Status = (NTSTATUS)LdrRelocateImage(NewImageAddress,
  5187. (CONST PCHAR)"SYSLDR",
  5188. (ULONG)STATUS_SUCCESS,
  5189. (ULONG)STATUS_CONFLICTING_ADDRESSES,
  5190. (ULONG)STATUS_INVALID_IMAGE_FORMAT
  5191. );
  5192. if (!NT_SUCCESS(Status)) {
  5193. if (MmMakeLowMemory == TRUE) {
  5194. while (PointerPte < LastPte) {
  5195. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  5196. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5197. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  5198. MI_SET_PFN_DELETED (Pfn1);
  5199. MiDecrementShareCountOnly (PageFrameIndex);
  5200. PointerPte += 1;
  5201. }
  5202. PointerPte -= NumberOfPtes;
  5203. }
  5204. MiReleaseSystemPtes (PointerPte, NumberOfPtes, SystemPteSpace);
  5205. if (StopMoving == TRUE) {
  5206. MmMakeLowMemory = FALSE;
  5207. }
  5208. continue;
  5209. }
  5210. }
  5211. //
  5212. // Update the IATs for all other loaded modules that reference this one.
  5213. //
  5214. NonRelocatedVa = (PCHAR) DataTableEntry->DllBase;
  5215. DataTableEntry->DllBase = NewImageAddress;
  5216. MiUpdateThunks (LoaderBlock,
  5217. LoaderImageAddress,
  5218. NewImageAddress,
  5219. DataTableEntry->SizeOfImage);
  5220. //
  5221. // Update the loaded module list entry.
  5222. //
  5223. DataTableEntry->Flags |= LDRP_SYSTEM_MAPPED;
  5224. DataTableEntry->DllBase = NewImageAddress;
  5225. DataTableEntry->EntryPoint =
  5226. (PVOID)((PCHAR)NewImageAddress + NtHeader->OptionalHeader.AddressOfEntryPoint);
  5227. DataTableEntry->SizeOfImage = NumberOfPtes << PAGE_SHIFT;
  5228. //
  5229. // Update the PFNs of the image to support trimming.
  5230. // Note that the loader addresses are freed now so no references
  5231. // to it are permitted after this point.
  5232. //
  5233. LoaderPte = MiGetPteAddress (NonRelocatedVa);
  5234. LOCK_PFN (OldIrql);
  5235. while (PointerPte < LastPte) {
  5236. ASSERT (PointerPte->u.Hard.Valid == 1);
  5237. if (MmMakeLowMemory == TRUE) {
  5238. ASSERT (LoaderPte->u.Hard.Valid == 1);
  5239. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (LoaderPte);
  5240. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5241. //
  5242. // Decrement the share count on the original page table
  5243. // page so it can be freed.
  5244. //
  5245. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  5246. MI_SET_PFN_DELETED (Pfn1);
  5247. MiDecrementShareCountOnly (PageFrameIndex);
  5248. LoaderPte += 1;
  5249. }
  5250. else {
  5251. PageFrameIndex = MI_GET_PAGE_FRAME_FROM_PTE (PointerPte);
  5252. Pfn1 = MI_PFN_ELEMENT (PageFrameIndex);
  5253. //
  5254. // Decrement the share count on the original page table
  5255. // page so it can be freed.
  5256. //
  5257. MiDecrementShareAndValidCount (Pfn1->u4.PteFrame);
  5258. *Pfn1->PteAddress = ZeroPte;
  5259. //
  5260. // Chain the PFN entry to its new page table.
  5261. //
  5262. PteFramePointer = MiGetPteAddress(PointerPte);
  5263. PteFramePage = MI_GET_PAGE_FRAME_FROM_PTE (PteFramePointer);
  5264. Pfn1->u4.PteFrame = PteFramePage;
  5265. Pfn1->PteAddress = PointerPte;
  5266. //
  5267. // Increment the share count for the page table page that now
  5268. // contains the PTE that was copied.
  5269. //
  5270. Pfn2 = MI_PFN_ELEMENT (PteFramePage);
  5271. Pfn2->u2.ShareCount += 1;
  5272. }
  5273. PointerPte += 1;
  5274. }
  5275. UNLOCK_PFN (OldIrql);
  5276. //
  5277. // The physical pages mapping the relocation section are freed
  5278. // later with the rest of the initialization code spanned by the
  5279. // DataTableEntry->SizeOfImage.
  5280. //
  5281. if (StopMoving == TRUE) {
  5282. MmMakeLowMemory = FALSE;
  5283. }
  5284. }
  5285. }
  5286. #if defined (_X86_)
  5287. PMMPTE MiKernelResourceStartPte;
  5288. PMMPTE MiKernelResourceEndPte;
  5289. #endif
  5290. VOID
  5291. MiLocateKernelSections (
  5292. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  5293. )
  5294. /*++
  5295. Routine Description:
  5296. This function locates the resource section in the kernel so it can be
  5297. made readwrite if we bugcheck later, as the bugcheck code will write
  5298. into it.
  5299. Arguments:
  5300. DataTableEntry - Supplies the kernel's data table entry.
  5301. Return Value:
  5302. None.
  5303. Environment:
  5304. Kernel mode, Phase 0 Initialization.
  5305. --*/
  5306. {
  5307. PVOID CurrentBase;
  5308. PIMAGE_NT_HEADERS NtHeader;
  5309. PIMAGE_SECTION_HEADER SectionTableEntry;
  5310. LONG i;
  5311. PMMPTE PointerPte;
  5312. PVOID SectionBaseAddress;
  5313. CurrentBase = (PVOID)DataTableEntry->DllBase;
  5314. NtHeader = RtlImageNtHeader(CurrentBase);
  5315. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  5316. sizeof(ULONG) +
  5317. sizeof(IMAGE_FILE_HEADER) +
  5318. NtHeader->FileHeader.SizeOfOptionalHeader);
  5319. //
  5320. // From the image header, locate the section named '.rsrc'.
  5321. //
  5322. i = NtHeader->FileHeader.NumberOfSections;
  5323. PointerPte = NULL;
  5324. while (i > 0) {
  5325. SectionBaseAddress = SECTION_BASE_ADDRESS(SectionTableEntry);
  5326. #if defined (_X86_)
  5327. if (*(PULONG)SectionTableEntry->Name == 'rsr.') {
  5328. MiKernelResourceStartPte = MiGetPteAddress ((ULONG_PTR)CurrentBase +
  5329. SectionTableEntry->VirtualAddress);
  5330. MiKernelResourceEndPte = MiGetPteAddress (ROUND_TO_PAGES((ULONG_PTR)CurrentBase +
  5331. SectionTableEntry->VirtualAddress +
  5332. SectionTableEntry->SizeOfRawData));
  5333. break;
  5334. }
  5335. #endif
  5336. if (*(PULONG)SectionTableEntry->Name == 'LOOP') {
  5337. if (*(PULONG)&SectionTableEntry->Name[4] == 'EDOC') {
  5338. ExPoolCodeStart = (PVOID)((ULONG_PTR)CurrentBase +
  5339. SectionTableEntry->VirtualAddress);
  5340. ExPoolCodeEnd = (PVOID)((ULONG_PTR)CurrentBase +
  5341. SectionTableEntry->VirtualAddress +
  5342. SectionTableEntry->SizeOfRawData);
  5343. }
  5344. else if (*(PUSHORT)&SectionTableEntry->Name[4] == 'IM') {
  5345. MmPoolCodeStart = (PVOID)((ULONG_PTR)CurrentBase +
  5346. SectionTableEntry->VirtualAddress);
  5347. MmPoolCodeEnd = (PVOID)((ULONG_PTR)CurrentBase +
  5348. SectionTableEntry->VirtualAddress +
  5349. SectionTableEntry->SizeOfRawData);
  5350. }
  5351. }
  5352. else if ((*(PULONG)SectionTableEntry->Name == 'YSIM') &&
  5353. (*(PULONG)&SectionTableEntry->Name[4] == 'ETPS')) {
  5354. MmPteCodeStart = (PVOID)((ULONG_PTR)CurrentBase +
  5355. SectionTableEntry->VirtualAddress);
  5356. MmPteCodeEnd = (PVOID)((ULONG_PTR)CurrentBase +
  5357. SectionTableEntry->VirtualAddress +
  5358. SectionTableEntry->SizeOfRawData);
  5359. }
  5360. i -= 1;
  5361. SectionTableEntry += 1;
  5362. }
  5363. }
  5364. VOID
  5365. MmMakeKernelResourceSectionWritable (
  5366. VOID
  5367. )
  5368. /*++
  5369. Routine Description:
  5370. This function makes the kernel's resource section readwrite so the bugcheck
  5371. code can write into it.
  5372. Arguments:
  5373. None.
  5374. Return Value:
  5375. None.
  5376. Environment:
  5377. Kernel mode. Any IRQL.
  5378. --*/
  5379. {
  5380. #if defined (_X86_)
  5381. MMPTE TempPte;
  5382. MMPTE PteContents;
  5383. PMMPTE PointerPte;
  5384. if (MiKernelResourceStartPte == NULL) {
  5385. return;
  5386. }
  5387. PointerPte = MiKernelResourceStartPte;
  5388. if (MI_IS_PHYSICAL_ADDRESS (MiGetVirtualAddressMappedByPte (PointerPte))) {
  5389. //
  5390. // Mapped physically, doesn't need to be made readwrite.
  5391. //
  5392. return;
  5393. }
  5394. //
  5395. // Since the entry state and IRQL are unknown, just go through the
  5396. // PTEs without a lock and make them all readwrite.
  5397. //
  5398. do {
  5399. PteContents = *PointerPte;
  5400. #if defined(NT_UP)
  5401. if (PteContents.u.Hard.Write == 0)
  5402. #else
  5403. if (PteContents.u.Hard.Writable == 0)
  5404. #endif
  5405. {
  5406. MI_MAKE_VALID_PTE (TempPte,
  5407. PteContents.u.Hard.PageFrameNumber,
  5408. MM_READWRITE,
  5409. PointerPte);
  5410. #if !defined(NT_UP)
  5411. TempPte.u.Hard.Writable = 1;
  5412. #endif
  5413. MI_WRITE_VALID_PTE_NEW_PROTECTION (PointerPte, TempPte);
  5414. }
  5415. PointerPte += 1;
  5416. } while (PointerPte < MiKernelResourceEndPte);
  5417. //
  5418. // Don't do this more than once.
  5419. //
  5420. MiKernelResourceStartPte = NULL;
  5421. //
  5422. // Only flush this processor as the state of the others is unknown.
  5423. //
  5424. KeFlushCurrentTb ();
  5425. #endif
  5426. }
  5427. #ifdef i386
  5428. PVOID PsNtosImageBase = (PVOID)0x80100000;
  5429. #else
  5430. PVOID PsNtosImageBase;
  5431. #endif
  5432. #if DBG
  5433. PVOID PsNtosImageEnd;
  5434. #endif
  5435. #if defined(_AMD64_) // || defined(_IA64_)
  5436. INVERTED_FUNCTION_TABLE PsInvertedFunctionTable = {
  5437. 0, MAXIMUM_INVERTED_FUNCTION_TABLE_SIZE, FALSE};
  5438. #endif
  5439. LIST_ENTRY PsLoadedModuleList;
  5440. ERESOURCE PsLoadedModuleResource;
  5441. LOGICAL
  5442. MiInitializeLoadedModuleList (
  5443. IN PLOADER_PARAMETER_BLOCK LoaderBlock
  5444. )
  5445. /*++
  5446. Routine Description:
  5447. This function initializes the loaded module list based on the LoaderBlock.
  5448. Arguments:
  5449. LoaderBlock - Supplies a pointer to the system loader block.
  5450. Return Value:
  5451. None.
  5452. Environment:
  5453. Kernel mode, Phase 0 Initialization.
  5454. --*/
  5455. {
  5456. SIZE_T DataTableEntrySize;
  5457. PLIST_ENTRY NextEntry;
  5458. PKLDR_DATA_TABLE_ENTRY DataTableEntry1;
  5459. PKLDR_DATA_TABLE_ENTRY DataTableEntry2;
  5460. //
  5461. // Initialize the loaded module list executive resource and spin lock.
  5462. //
  5463. ExInitializeResourceLite (&PsLoadedModuleResource);
  5464. KeInitializeSpinLock (&PsLoadedModuleSpinLock);
  5465. InitializeListHead (&PsLoadedModuleList);
  5466. //
  5467. // Scan the loaded module list and allocate and initialize a data table
  5468. // entry for each module. The data table entry is inserted in the loaded
  5469. // module list and the initialization order list in the order specified
  5470. // in the loader parameter block. The data table entry is inserted in the
  5471. // memory order list in memory order.
  5472. //
  5473. NextEntry = LoaderBlock->LoadOrderListHead.Flink;
  5474. DataTableEntry2 = CONTAINING_RECORD(NextEntry,
  5475. KLDR_DATA_TABLE_ENTRY,
  5476. InLoadOrderLinks);
  5477. PsNtosImageBase = DataTableEntry2->DllBase;
  5478. #if DBG
  5479. PsNtosImageEnd = (PVOID) ((ULONG_PTR) DataTableEntry2->DllBase + DataTableEntry2->SizeOfImage);
  5480. #endif
  5481. MiLocateKernelSections (DataTableEntry2);
  5482. while (NextEntry != &LoaderBlock->LoadOrderListHead) {
  5483. DataTableEntry2 = CONTAINING_RECORD(NextEntry,
  5484. KLDR_DATA_TABLE_ENTRY,
  5485. InLoadOrderLinks);
  5486. //
  5487. // Allocate a data table entry.
  5488. //
  5489. DataTableEntrySize = sizeof (KLDR_DATA_TABLE_ENTRY) +
  5490. DataTableEntry2->BaseDllName.MaximumLength + sizeof(UNICODE_NULL);
  5491. DataTableEntry1 = ExAllocatePoolWithTag (NonPagedPool,
  5492. DataTableEntrySize,
  5493. 'dLmM');
  5494. if (DataTableEntry1 == NULL) {
  5495. return FALSE;
  5496. }
  5497. ASSERT (sizeof(KLDR_DATA_TABLE_ENTRY) == sizeof(LDR_DATA_TABLE_ENTRY));
  5498. //
  5499. // Copy the data table entry.
  5500. //
  5501. *DataTableEntry1 = *DataTableEntry2;
  5502. //
  5503. // Clear fields we may use later so they don't inherit irrelevant
  5504. // loader values.
  5505. //
  5506. ((PKLDR_DATA_TABLE_ENTRY)DataTableEntry1)->NonPagedDebugInfo = NULL;
  5507. DataTableEntry1->FullDllName.Buffer = ExAllocatePoolWithTag (PagedPool,
  5508. DataTableEntry2->FullDllName.MaximumLength + sizeof(UNICODE_NULL),
  5509. 'TDmM');
  5510. if (DataTableEntry1->FullDllName.Buffer == NULL) {
  5511. ExFreePool (DataTableEntry1);
  5512. return FALSE;
  5513. }
  5514. DataTableEntry1->BaseDllName.Buffer = (PWSTR)((ULONG_PTR)DataTableEntry1 + sizeof (KLDR_DATA_TABLE_ENTRY));
  5515. //
  5516. // Copy the strings.
  5517. //
  5518. RtlCopyMemory (DataTableEntry1->FullDllName.Buffer,
  5519. DataTableEntry2->FullDllName.Buffer,
  5520. DataTableEntry1->FullDllName.MaximumLength);
  5521. RtlCopyMemory (DataTableEntry1->BaseDllName.Buffer,
  5522. DataTableEntry2->BaseDllName.Buffer,
  5523. DataTableEntry1->BaseDllName.MaximumLength);
  5524. DataTableEntry1->BaseDllName.Buffer[DataTableEntry1->BaseDllName.Length/sizeof(WCHAR)] = UNICODE_NULL;
  5525. //
  5526. // Insert the data table entry in the load order list in the order
  5527. // they are specified.
  5528. //
  5529. InsertTailList(&PsLoadedModuleList,
  5530. &DataTableEntry1->InLoadOrderLinks);
  5531. #if defined(_AMD64_) // || defined(_IA64_)
  5532. RtlInsertInvertedFunctionTable (&PsInvertedFunctionTable,
  5533. DataTableEntry1->DllBase,
  5534. DataTableEntry1->SizeOfImage);
  5535. #endif
  5536. NextEntry = NextEntry->Flink;
  5537. }
  5538. MiBuildImportsForBootDrivers ();
  5539. return TRUE;
  5540. }
  5541. NTSTATUS
  5542. MmCallDllInitialize (
  5543. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry,
  5544. IN PLIST_ENTRY ModuleListHead
  5545. )
  5546. /*++
  5547. Routine Description:
  5548. This function calls the DLL's initialize routine.
  5549. Arguments:
  5550. DataTableEntry - Supplies the kernel's data table entry.
  5551. Return Value:
  5552. Various NTSTATUS error codes.
  5553. Environment:
  5554. Kernel mode.
  5555. --*/
  5556. {
  5557. NTSTATUS st;
  5558. PWCHAR Dot;
  5559. PMM_DLL_INITIALIZE Func;
  5560. UNICODE_STRING RegistryPath;
  5561. UNICODE_STRING ImportName;
  5562. ULONG ThunksAdded;
  5563. Func = (PMM_DLL_INITIALIZE)(ULONG_PTR)MiLocateExportName (DataTableEntry->DllBase, "DllInitialize");
  5564. if (!Func) {
  5565. return STATUS_SUCCESS;
  5566. }
  5567. ImportName.MaximumLength = DataTableEntry->BaseDllName.Length;
  5568. ImportName.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  5569. ImportName.MaximumLength,
  5570. 'TDmM');
  5571. if (ImportName.Buffer == NULL) {
  5572. return STATUS_INSUFFICIENT_RESOURCES;
  5573. }
  5574. ImportName.Length = DataTableEntry->BaseDllName.Length;
  5575. RtlCopyMemory (ImportName.Buffer,
  5576. DataTableEntry->BaseDllName.Buffer,
  5577. ImportName.Length);
  5578. RegistryPath.MaximumLength = (USHORT)(CmRegistryMachineSystemCurrentControlSetServices.Length +
  5579. ImportName.Length +
  5580. 2*sizeof(WCHAR));
  5581. RegistryPath.Buffer = ExAllocatePoolWithTag (NonPagedPool,
  5582. RegistryPath.MaximumLength,
  5583. 'TDmM');
  5584. if (RegistryPath.Buffer == NULL) {
  5585. ExFreePool (ImportName.Buffer);
  5586. return STATUS_INSUFFICIENT_RESOURCES;
  5587. }
  5588. RegistryPath.Length = CmRegistryMachineSystemCurrentControlSetServices.Length;
  5589. RtlCopyMemory (RegistryPath.Buffer,
  5590. CmRegistryMachineSystemCurrentControlSetServices.Buffer,
  5591. CmRegistryMachineSystemCurrentControlSetServices.Length);
  5592. RtlAppendUnicodeToString (&RegistryPath, (const PUSHORT)L"\\");
  5593. Dot = wcschr (ImportName.Buffer, L'.');
  5594. if (Dot) {
  5595. ImportName.Length = (USHORT)((Dot - ImportName.Buffer) * sizeof(WCHAR));
  5596. }
  5597. RtlAppendUnicodeStringToString (&RegistryPath, &ImportName);
  5598. ExFreePool (ImportName.Buffer);
  5599. //
  5600. // Save the number of verifier thunks currently added so we know
  5601. // if this activation adds any. To extend the thunk list, the module
  5602. // performs an NtSetSystemInformation call which calls back to the
  5603. // verifier's MmAddVerifierThunks, which increments MiVerifierThunksAdded.
  5604. //
  5605. ThunksAdded = MiVerifierThunksAdded;
  5606. //
  5607. // Invoke the DLL's initialization routine.
  5608. //
  5609. st = Func (&RegistryPath);
  5610. ExFreePool (RegistryPath.Buffer);
  5611. //
  5612. // If the module's initialization routine succeeded, and if it extended
  5613. // the verifier thunk list, and this is boot time, reapply the verifier
  5614. // to the loaded modules.
  5615. //
  5616. // Note that boot time is the special case because after boot time, Mm
  5617. // loads all the DLLs itself and a DLL initialize is thus guaranteed to
  5618. // complete and add its thunks before the importing driver load finishes.
  5619. // Since the importing driver is only thunked after its load finishes,
  5620. // ordering implicitly guarantees that all DLL-registered thunks are
  5621. // properly factored in to the importing driver.
  5622. //
  5623. // Boot time is special because the loader (not the Mm) already loaded
  5624. // the DLLs *AND* the importing drivers so we have to look over our
  5625. // shoulder and make it all right after the fact.
  5626. //
  5627. if ((NT_SUCCESS(st)) &&
  5628. (MiFirstDriverLoadEver == 0) &&
  5629. (MiVerifierThunksAdded != ThunksAdded)) {
  5630. MiReApplyVerifierToLoadedModules (ModuleListHead);
  5631. }
  5632. return st;
  5633. }
  5634. NTKERNELAPI
  5635. PVOID
  5636. MmGetSystemRoutineAddress (
  5637. IN PUNICODE_STRING SystemRoutineName
  5638. )
  5639. /*++
  5640. Routine Description:
  5641. This function returns the address of the argument function pointer if
  5642. it is in the kernel or HAL, NULL if it is not.
  5643. Arguments:
  5644. SystemRoutineName - Supplies the name of the desired routine.
  5645. Return Value:
  5646. Non-NULL function pointer if successful. NULL if not.
  5647. Environment:
  5648. Kernel mode, PASSIVE_LEVEL, arbitrary process context.
  5649. --*/
  5650. {
  5651. PKTHREAD CurrentThread;
  5652. NTSTATUS Status;
  5653. PKLDR_DATA_TABLE_ENTRY DataTableEntry;
  5654. ANSI_STRING AnsiString;
  5655. PLIST_ENTRY NextEntry;
  5656. UNICODE_STRING KernelString;
  5657. UNICODE_STRING HalString;
  5658. PVOID FunctionAddress;
  5659. LOGICAL Found;
  5660. ULONG EntriesChecked;
  5661. ASSERT (KeGetCurrentIrql() == PASSIVE_LEVEL);
  5662. EntriesChecked = 0;
  5663. FunctionAddress = NULL;
  5664. RtlInitUnicodeString (&KernelString, (const PUSHORT)L"ntoskrnl.exe");
  5665. RtlInitUnicodeString (&HalString, (const PUSHORT)L"hal.dll");
  5666. do {
  5667. Status = RtlUnicodeStringToAnsiString (&AnsiString,
  5668. SystemRoutineName,
  5669. TRUE);
  5670. if (NT_SUCCESS (Status)) {
  5671. break;
  5672. }
  5673. KeDelayExecutionThread (KernelMode, FALSE, (PLARGE_INTEGER)&MmShortTime);
  5674. } while (TRUE);
  5675. //
  5676. // Arbitrary process context so prevent suspend APCs now.
  5677. //
  5678. CurrentThread = KeGetCurrentThread ();
  5679. KeEnterCriticalRegionThread (CurrentThread);
  5680. ExAcquireResourceSharedLite (&PsLoadedModuleResource, TRUE);
  5681. //
  5682. // Check only the kernel and the HAL for exports.
  5683. //
  5684. NextEntry = PsLoadedModuleList.Flink;
  5685. while (NextEntry != &PsLoadedModuleList) {
  5686. Found = FALSE;
  5687. DataTableEntry = CONTAINING_RECORD(NextEntry,
  5688. KLDR_DATA_TABLE_ENTRY,
  5689. InLoadOrderLinks);
  5690. if (RtlEqualUnicodeString (&KernelString,
  5691. &DataTableEntry->BaseDllName,
  5692. TRUE)) {
  5693. Found = TRUE;
  5694. EntriesChecked += 1;
  5695. }
  5696. else if (RtlEqualUnicodeString (&HalString,
  5697. &DataTableEntry->BaseDllName,
  5698. TRUE)) {
  5699. Found = TRUE;
  5700. EntriesChecked += 1;
  5701. }
  5702. if (Found == TRUE) {
  5703. FunctionAddress = MiFindExportedRoutineByName (DataTableEntry->DllBase,
  5704. &AnsiString);
  5705. if (FunctionAddress != NULL) {
  5706. break;
  5707. }
  5708. if (EntriesChecked == 2) {
  5709. break;
  5710. }
  5711. }
  5712. NextEntry = NextEntry->Flink;
  5713. }
  5714. ExReleaseResourceLite (&PsLoadedModuleResource);
  5715. KeLeaveCriticalRegionThread (CurrentThread);
  5716. RtlFreeAnsiString (&AnsiString);
  5717. return FunctionAddress;
  5718. }
  5719. PVOID
  5720. MiFindExportedRoutineByName (
  5721. IN PVOID DllBase,
  5722. IN PANSI_STRING AnsiImageRoutineName
  5723. )
  5724. /*++
  5725. Routine Description:
  5726. This function searches the argument module looking for the requested
  5727. exported function name.
  5728. Arguments:
  5729. DllBase - Supplies the base address of the requested module.
  5730. AnsiImageRoutineName - Supplies the ANSI routine name being searched for.
  5731. Return Value:
  5732. The virtual address of the requested routine or NULL if not found.
  5733. --*/
  5734. {
  5735. USHORT OrdinalNumber;
  5736. PULONG NameTableBase;
  5737. PUSHORT NameOrdinalTableBase;
  5738. PULONG Addr;
  5739. ULONG High;
  5740. ULONG Low;
  5741. ULONG Middle;
  5742. LONG Result;
  5743. ULONG ExportSize;
  5744. PVOID FunctionAddress;
  5745. PIMAGE_EXPORT_DIRECTORY ExportDirectory;
  5746. PAGED_CODE();
  5747. ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(
  5748. DllBase,
  5749. TRUE,
  5750. IMAGE_DIRECTORY_ENTRY_EXPORT,
  5751. &ExportSize
  5752. );
  5753. if (ExportDirectory == NULL) {
  5754. return NULL;
  5755. }
  5756. //
  5757. // Initialize the pointer to the array of RVA-based ansi export strings.
  5758. //
  5759. NameTableBase = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNames);
  5760. //
  5761. // Initialize the pointer to the array of USHORT ordinal numbers.
  5762. //
  5763. NameOrdinalTableBase = (PUSHORT)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfNameOrdinals);
  5764. //
  5765. // Lookup the desired name in the name table using a binary search.
  5766. //
  5767. Low = 0;
  5768. High = ExportDirectory->NumberOfNames - 1;
  5769. //
  5770. // Initializing Middle is not needed for correctness, but without it
  5771. // the compiler cannot compile this code W4 to check for use of
  5772. // uninitialized variables.
  5773. //
  5774. Middle = 0;
  5775. while (High >= Low) {
  5776. //
  5777. // Compute the next probe index and compare the import name
  5778. // with the export name entry.
  5779. //
  5780. Middle = (Low + High) >> 1;
  5781. Result = strcmp (AnsiImageRoutineName->Buffer,
  5782. (PCHAR)DllBase + NameTableBase[Middle]);
  5783. if (Result < 0) {
  5784. High = Middle - 1;
  5785. }
  5786. else if (Result > 0) {
  5787. Low = Middle + 1;
  5788. }
  5789. else {
  5790. break;
  5791. }
  5792. }
  5793. //
  5794. // If the high index is less than the low index, then a matching
  5795. // table entry was not found. Otherwise, get the ordinal number
  5796. // from the ordinal table.
  5797. //
  5798. if ((LONG)High < (LONG)Low) {
  5799. return NULL;
  5800. }
  5801. OrdinalNumber = NameOrdinalTableBase[Middle];
  5802. //
  5803. // If the OrdinalNumber is not within the Export Address Table,
  5804. // then this image does not implement the function. Return not found.
  5805. //
  5806. if ((ULONG)OrdinalNumber >= ExportDirectory->NumberOfFunctions) {
  5807. return NULL;
  5808. }
  5809. //
  5810. // Index into the array of RVA export addresses by ordinal number.
  5811. //
  5812. Addr = (PULONG)((PCHAR)DllBase + (ULONG)ExportDirectory->AddressOfFunctions);
  5813. FunctionAddress = (PVOID)((PCHAR)DllBase + Addr[OrdinalNumber]);
  5814. //
  5815. // Forwarders are not used by the kernel and HAL to each other.
  5816. //
  5817. ASSERT ((FunctionAddress <= (PVOID)ExportDirectory) ||
  5818. (FunctionAddress >= (PVOID)((PCHAR)ExportDirectory + ExportSize)));
  5819. return FunctionAddress;
  5820. }
  5821. #if _MI_DEBUG_RONLY
  5822. PMMPTE MiSessionDataStartPte;
  5823. PMMPTE MiSessionDataEndPte;
  5824. VOID
  5825. MiAssertNotSessionData (
  5826. IN PMMPTE PointerPte
  5827. )
  5828. {
  5829. if (MI_IS_SESSION_IMAGE_PTE (PointerPte)) {
  5830. if ((PointerPte >= MiSessionDataStartPte) &&
  5831. (PointerPte <= MiSessionDataEndPte)) {
  5832. KeBugCheckEx (MEMORY_MANAGEMENT,
  5833. 0x41287,
  5834. (ULONG_PTR) PointerPte,
  5835. 0,
  5836. 0);
  5837. }
  5838. }
  5839. }
  5840. VOID
  5841. MiLogSessionDataStart (
  5842. IN PKLDR_DATA_TABLE_ENTRY DataTableEntry
  5843. )
  5844. {
  5845. LONG i;
  5846. PVOID CurrentBase;
  5847. PIMAGE_NT_HEADERS NtHeader;
  5848. PIMAGE_SECTION_HEADER SectionTableEntry;
  5849. PVOID DataStart;
  5850. PVOID DataEnd;
  5851. if (MiSessionDataStartPte != NULL) {
  5852. return;
  5853. }
  5854. //
  5855. // Crack the image header to mark the data.
  5856. //
  5857. CurrentBase = (PVOID)DataTableEntry->DllBase;
  5858. NtHeader = RtlImageNtHeader(CurrentBase);
  5859. SectionTableEntry = (PIMAGE_SECTION_HEADER)((PCHAR)NtHeader +
  5860. sizeof(ULONG) +
  5861. sizeof(IMAGE_FILE_HEADER) +
  5862. NtHeader->FileHeader.SizeOfOptionalHeader);
  5863. i = NtHeader->FileHeader.NumberOfSections;
  5864. while (i > 0) {
  5865. //
  5866. // Save the start and end of the data section.
  5867. //
  5868. if ((*(PULONG)SectionTableEntry->Name == 0x7461642e) &&
  5869. (*(PULONG)&SectionTableEntry->Name[4] == 0x61)) {
  5870. DataStart = (PVOID)((PCHAR)CurrentBase + SectionTableEntry->VirtualAddress);
  5871. DataEnd = (PVOID)((PCHAR)DataStart + SectionTableEntry->SizeOfRawData - 1);
  5872. MiSessionDataStartPte = MiGetPteAddress (DataStart);
  5873. MiSessionDataEndPte = MiGetPteAddress (DataEnd);
  5874. break;
  5875. }
  5876. i -= 1;
  5877. SectionTableEntry += 1;
  5878. }
  5879. }
  5880. #endif