1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS NT User Mode Library
4 * FILE: dll/ntdll/ldr/ldrapi.c
5 * PURPOSE: PE Loader Public APIs
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Aleksey Bragin (aleksey@reactos.org)
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntdll.h>
13
14 #define NDEBUG
15 #include <debug.h>
16
17 /* GLOBALS *******************************************************************/
18
19 LIST_ENTRY LdrpUnloadHead;
20 LONG LdrpLoaderLockAcquisitionCount;
21 BOOLEAN LdrpShowRecursiveLoads, LdrpBreakOnRecursiveDllLoads;
22 UNICODE_STRING LdrApiDefaultExtension = RTL_CONSTANT_STRING(L".DLL");
23 ULONG AlternateResourceModuleCount;
24 extern PLDR_MANIFEST_PROBER_ROUTINE LdrpManifestProberRoutine;
25
26 /* FUNCTIONS *****************************************************************/
27
28 NTSTATUS
29 NTAPI
LdrFindCreateProcessManifest(IN ULONG Flags,IN PVOID Image,IN PVOID IdPath,IN ULONG IdPathLength,IN PVOID OutDataEntry)30 LdrFindCreateProcessManifest(IN ULONG Flags,
31 IN PVOID Image,
32 IN PVOID IdPath,
33 IN ULONG IdPathLength,
34 IN PVOID OutDataEntry)
35 {
36 UNIMPLEMENTED;
37 return STATUS_NOT_IMPLEMENTED;
38 }
39
40 NTSTATUS
41 NTAPI
LdrDestroyOutOfProcessImage(IN PVOID Image)42 LdrDestroyOutOfProcessImage(IN PVOID Image)
43 {
44 UNIMPLEMENTED;
45 return STATUS_NOT_IMPLEMENTED;
46 }
47
48 NTSTATUS
49 NTAPI
LdrCreateOutOfProcessImage(IN ULONG Flags,IN HANDLE ProcessHandle,IN HANDLE DllHandle,IN PVOID Unknown3)50 LdrCreateOutOfProcessImage(IN ULONG Flags,
51 IN HANDLE ProcessHandle,
52 IN HANDLE DllHandle,
53 IN PVOID Unknown3)
54 {
55 UNIMPLEMENTED;
56 return STATUS_NOT_IMPLEMENTED;
57 }
58
59 NTSTATUS
60 NTAPI
LdrAccessOutOfProcessResource(IN PVOID Unknown,IN PVOID Image,IN PVOID Unknown1,IN PVOID Unknown2,IN PVOID Unknown3)61 LdrAccessOutOfProcessResource(IN PVOID Unknown,
62 IN PVOID Image,
63 IN PVOID Unknown1,
64 IN PVOID Unknown2,
65 IN PVOID Unknown3)
66 {
67 UNIMPLEMENTED;
68 return STATUS_NOT_IMPLEMENTED;
69 }
70
71 VOID
72 NTAPI
LdrSetDllManifestProber(_In_ PLDR_MANIFEST_PROBER_ROUTINE Routine)73 LdrSetDllManifestProber(
74 _In_ PLDR_MANIFEST_PROBER_ROUTINE Routine)
75 {
76 LdrpManifestProberRoutine = Routine;
77 }
78
79 BOOLEAN
80 NTAPI
LdrAlternateResourcesEnabled(VOID)81 LdrAlternateResourcesEnabled(VOID)
82 {
83 /* ReactOS does not support this */
84 return FALSE;
85 }
86
87 FORCEINLINE
88 ULONG_PTR
LdrpMakeCookie(VOID)89 LdrpMakeCookie(VOID)
90 {
91 /* Generate a cookie */
92 return (((ULONG_PTR)NtCurrentTeb()->RealClientId.UniqueThread & 0xFFF) << 16) |
93 (_InterlockedIncrement(&LdrpLoaderLockAcquisitionCount) & 0xFFFF);
94 }
95
96 /*
97 * @implemented
98 */
99 NTSTATUS
100 NTAPI
LdrUnlockLoaderLock(_In_ ULONG Flags,_In_opt_ ULONG_PTR Cookie)101 LdrUnlockLoaderLock(
102 _In_ ULONG Flags,
103 _In_opt_ ULONG_PTR Cookie)
104 {
105 NTSTATUS Status = STATUS_SUCCESS;
106
107 DPRINT("LdrUnlockLoaderLock(%x %Ix)\n", Flags, Cookie);
108
109 /* Check for valid flags */
110 if (Flags & ~LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
111 {
112 /* Flags are invalid, check how to fail */
113 if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
114 {
115 /* The caller wants us to raise status */
116 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
117 }
118
119 /* A normal failure */
120 return STATUS_INVALID_PARAMETER_1;
121 }
122
123 /* If we don't have a cookie, just return */
124 if (!Cookie) return STATUS_SUCCESS;
125
126 /* Validate the cookie */
127 if ((Cookie & 0xF0000000) ||
128 ((Cookie >> 16) ^ (HandleToUlong(NtCurrentTeb()->RealClientId.UniqueThread) & 0xFFF)))
129 {
130 DPRINT1("LdrUnlockLoaderLock() called with an invalid cookie!\n");
131
132 /* Invalid cookie, check how to fail */
133 if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
134 {
135 /* The caller wants us to raise status */
136 RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
137 }
138
139 /* A normal failure */
140 return STATUS_INVALID_PARAMETER_2;
141 }
142
143 /* Ready to release the lock */
144 if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
145 {
146 /* Do a direct leave */
147 RtlLeaveCriticalSection(&LdrpLoaderLock);
148 }
149 else
150 {
151 /* Wrap this in SEH, since we're not supposed to raise */
152 _SEH2_TRY
153 {
154 /* Leave the lock */
155 RtlLeaveCriticalSection(&LdrpLoaderLock);
156 }
157 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
158 {
159 /* We should use the LDR Filter instead */
160 Status = _SEH2_GetExceptionCode();
161 }
162 _SEH2_END;
163 }
164
165 /* All done */
166 return Status;
167 }
168
169 /*
170 * @implemented
171 */
172 NTSTATUS
173 NTAPI
LdrLockLoaderLock(_In_ ULONG Flags,_Out_opt_ PULONG Disposition,_Out_opt_ PULONG_PTR Cookie)174 LdrLockLoaderLock(
175 _In_ ULONG Flags,
176 _Out_opt_ PULONG Disposition,
177 _Out_opt_ PULONG_PTR Cookie)
178 {
179 NTSTATUS Status = STATUS_SUCCESS;
180 BOOLEAN InInit = LdrpInLdrInit;
181
182 DPRINT("LdrLockLoaderLock(%x %p %p)\n", Flags, Disposition, Cookie);
183
184 /* Zero out the outputs */
185 if (Disposition) *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_INVALID;
186 if (Cookie) *Cookie = 0;
187
188 /* Validate the flags */
189 if (Flags & ~(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS |
190 LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY))
191 {
192 /* Flags are invalid, check how to fail */
193 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
194 {
195 /* The caller wants us to raise status */
196 RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
197 }
198
199 /* A normal failure */
200 return STATUS_INVALID_PARAMETER_1;
201 }
202
203 /* Make sure we got a cookie */
204 if (!Cookie)
205 {
206 /* No cookie check how to fail */
207 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
208 {
209 /* The caller wants us to raise status */
210 RtlRaiseStatus(STATUS_INVALID_PARAMETER_3);
211 }
212
213 /* A normal failure */
214 return STATUS_INVALID_PARAMETER_3;
215 }
216
217 /* If the flag is set, make sure we have a valid pointer to use */
218 if ((Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY) && !(Disposition))
219 {
220 /* No pointer to return the data to */
221 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
222 {
223 /* The caller wants us to raise status */
224 RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
225 }
226
227 /* Fail */
228 return STATUS_INVALID_PARAMETER_2;
229 }
230
231 /* Return now if we are in the init phase */
232 if (InInit) return STATUS_SUCCESS;
233
234 /* Check what locking semantic to use */
235 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
236 {
237 /* Check if we should enter or simply try */
238 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
239 {
240 /* Do a try */
241 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
242 {
243 /* It's locked */
244 *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
245 }
246 else
247 {
248 /* It worked */
249 *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
250 *Cookie = LdrpMakeCookie();
251 }
252 }
253 else
254 {
255 /* Do a enter */
256 RtlEnterCriticalSection(&LdrpLoaderLock);
257
258 /* See if result was requested */
259 if (Disposition) *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
260 *Cookie = LdrpMakeCookie();
261 }
262 }
263 else
264 {
265 /* Wrap this in SEH, since we're not supposed to raise */
266 _SEH2_TRY
267 {
268 /* Check if we should enter or simply try */
269 if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY)
270 {
271 /* Do a try */
272 if (!RtlTryEnterCriticalSection(&LdrpLoaderLock))
273 {
274 /* It's locked */
275 *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
276 }
277 else
278 {
279 /* It worked */
280 *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
281 *Cookie = LdrpMakeCookie();
282 }
283 }
284 else
285 {
286 /* Do an enter */
287 RtlEnterCriticalSection(&LdrpLoaderLock);
288
289 /* See if result was requested */
290 if (Disposition) *Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
291 *Cookie = LdrpMakeCookie();
292 }
293 }
294 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
295 {
296 /* We should use the LDR Filter instead */
297 Status = _SEH2_GetExceptionCode();
298 }
299 _SEH2_END;
300 }
301
302 /* Return status */
303 return Status;
304 }
305
306 /*
307 * @implemented
308 */
309 NTSTATUS
310 NTAPI
311 DECLSPEC_HOTPATCH
LdrLoadDll(_In_opt_ PWSTR SearchPath,_In_opt_ PULONG DllCharacteristics,_In_ PUNICODE_STRING DllName,_Out_ PVOID * BaseAddress)312 LdrLoadDll(
313 _In_opt_ PWSTR SearchPath,
314 _In_opt_ PULONG DllCharacteristics,
315 _In_ PUNICODE_STRING DllName,
316 _Out_ PVOID *BaseAddress)
317 {
318 WCHAR StringBuffer[MAX_PATH];
319 UNICODE_STRING StaticString, DynamicString;
320 BOOLEAN RedirectedDll = FALSE;
321 NTSTATUS Status;
322 ULONG_PTR Cookie;
323 PUNICODE_STRING OldTldDll;
324 PTEB Teb = NtCurrentTeb();
325
326 /* Initialize the strings */
327 RtlInitEmptyUnicodeString(&StaticString, StringBuffer, sizeof(StringBuffer));
328 RtlInitEmptyUnicodeString(&DynamicString, NULL, 0);
329
330 Status = LdrpApplyFileNameRedirection(DllName, &LdrApiDefaultExtension, &StaticString, &DynamicString, &DllName, &RedirectedDll);
331
332 /* Lock the loader lock */
333 LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &Cookie);
334
335 /* Check if there's a TLD DLL being loaded */
336 OldTldDll = LdrpTopLevelDllBeingLoaded;
337 _SEH2_TRY
338 {
339
340 if (OldTldDll)
341 {
342 /* This is a recursive load, do something about it? */
343 if ((ShowSnaps) || (LdrpShowRecursiveLoads) || (LdrpBreakOnRecursiveDllLoads))
344 {
345 /* Print out debug messages */
346 DPRINT1("[%p, %p] LDR: Recursive DLL Load\n",
347 Teb->RealClientId.UniqueProcess,
348 Teb->RealClientId.UniqueThread);
349 DPRINT1("[%p, %p] Previous DLL being loaded \"%wZ\"\n",
350 Teb->RealClientId.UniqueProcess,
351 Teb->RealClientId.UniqueThread,
352 OldTldDll);
353 DPRINT1("[%p, %p] DLL being requested \"%wZ\"\n",
354 Teb->RealClientId.UniqueProcess,
355 Teb->RealClientId.UniqueThread,
356 DllName);
357
358 /* Was it initializing too? */
359 if (!LdrpCurrentDllInitializer)
360 {
361 DPRINT1("[%p, %p] LDR: No DLL Initializer was running\n",
362 Teb->RealClientId.UniqueProcess,
363 Teb->RealClientId.UniqueThread);
364 }
365 else
366 {
367 DPRINT1("[%p, %p] DLL whose initializer was currently running \"%wZ\"\n",
368 Teb->ClientId.UniqueProcess,
369 Teb->ClientId.UniqueThread,
370 &LdrpCurrentDllInitializer->BaseDllName);
371 }
372 }
373 }
374
375 /* Set this one as the TLD DLL being loaded*/
376 LdrpTopLevelDllBeingLoaded = DllName;
377
378 /* Load the DLL */
379 Status = LdrpLoadDll(RedirectedDll,
380 SearchPath,
381 DllCharacteristics,
382 DllName,
383 BaseAddress,
384 TRUE);
385 if (NT_SUCCESS(Status))
386 {
387 Status = STATUS_SUCCESS;
388 }
389 else if ((Status != STATUS_NO_SUCH_FILE) &&
390 (Status != STATUS_DLL_NOT_FOUND) &&
391 (Status != STATUS_OBJECT_NAME_NOT_FOUND) &&
392 (Status != STATUS_DLL_INIT_FAILED))
393 {
394 DbgPrintEx(DPFLTR_LDR_ID,
395 DPFLTR_WARNING_LEVEL,
396 "LDR: %s - failing because LdrpLoadDll(%wZ) returned status %x\n",
397 __FUNCTION__,
398 DllName,
399 Status);
400 }
401 }
402 _SEH2_FINALLY
403 {
404 /* Restore the old TLD DLL */
405 LdrpTopLevelDllBeingLoaded = OldTldDll;
406
407 /* Release the lock */
408 LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
409 }
410 _SEH2_END;
411
412 /* Do we have a redirect string? */
413 if (DynamicString.Buffer)
414 RtlFreeUnicodeString(&DynamicString);
415
416 /* Return */
417 return Status;
418 }
419
420 /*
421 * @implemented
422 */
423 NTSTATUS
424 NTAPI
LdrFindEntryForAddress(_In_ PVOID Address,_Out_ PLDR_DATA_TABLE_ENTRY * Module)425 LdrFindEntryForAddress(
426 _In_ PVOID Address,
427 _Out_ PLDR_DATA_TABLE_ENTRY *Module)
428 {
429 PLIST_ENTRY ListHead, NextEntry;
430 PLDR_DATA_TABLE_ENTRY LdrEntry;
431 PIMAGE_NT_HEADERS NtHeader;
432 PPEB_LDR_DATA Ldr = NtCurrentPeb()->Ldr;
433 ULONG_PTR DllBase, DllEnd;
434
435 DPRINT("LdrFindEntryForAddress(Address %p)\n", Address);
436
437 /* Nothing to do */
438 if (!Ldr) return STATUS_NO_MORE_ENTRIES;
439
440 /* Get the current entry */
441 LdrEntry = Ldr->EntryInProgress;
442 if (LdrEntry)
443 {
444 /* Get the NT Headers */
445 NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
446 if (NtHeader)
447 {
448 /* Get the Image Base */
449 DllBase = (ULONG_PTR)LdrEntry->DllBase;
450 DllEnd = DllBase + NtHeader->OptionalHeader.SizeOfImage;
451
452 /* Check if they match */
453 if (((ULONG_PTR)Address >= DllBase) &&
454 ((ULONG_PTR)Address < DllEnd))
455 {
456 /* Return it */
457 *Module = LdrEntry;
458 return STATUS_SUCCESS;
459 }
460 }
461 }
462
463 /* Loop the module list */
464 ListHead = &Ldr->InMemoryOrderModuleList;
465 NextEntry = ListHead->Flink;
466 while (NextEntry != ListHead)
467 {
468 /* Get the entry and NT Headers */
469 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
470 NtHeader = RtlImageNtHeader(LdrEntry->DllBase);
471 if (NtHeader)
472 {
473 /* Get the Image Base */
474 DllBase = (ULONG_PTR)LdrEntry->DllBase;
475 DllEnd = DllBase + NtHeader->OptionalHeader.SizeOfImage;
476
477 /* Check if they match */
478 if (((ULONG_PTR)Address >= DllBase) &&
479 ((ULONG_PTR)Address < DllEnd))
480 {
481 /* Return it */
482 *Module = LdrEntry;
483 return STATUS_SUCCESS;
484 }
485
486 /* Next Entry */
487 NextEntry = NextEntry->Flink;
488 }
489 }
490
491 /* Nothing found */
492 DbgPrintEx(DPFLTR_LDR_ID,
493 DPFLTR_WARNING_LEVEL,
494 "LDR: %s() exiting 0x%08lx\n",
495 __FUNCTION__,
496 STATUS_NO_MORE_ENTRIES);
497 return STATUS_NO_MORE_ENTRIES;
498 }
499
500 /*
501 * @implemented
502 */
503 NTSTATUS
504 NTAPI
LdrGetDllHandleEx(_In_ ULONG Flags,_In_opt_ PWSTR DllPath,_In_opt_ PULONG DllCharacteristics,_In_ PUNICODE_STRING DllName,_Out_opt_ PVOID * DllHandle)505 LdrGetDllHandleEx(
506 _In_ ULONG Flags,
507 _In_opt_ PWSTR DllPath,
508 _In_opt_ PULONG DllCharacteristics,
509 _In_ PUNICODE_STRING DllName,
510 _Out_opt_ PVOID *DllHandle)
511 {
512 NTSTATUS Status;
513 PLDR_DATA_TABLE_ENTRY LdrEntry;
514 UNICODE_STRING RedirectName, DynamicString, RawDllName;
515 PUNICODE_STRING pRedirectName, CompareName;
516 PWCHAR p1, p2, p3;
517 BOOLEAN Locked, RedirectedDll;
518 ULONG_PTR Cookie;
519 ULONG LoadFlag, Length;
520
521 /* Initialize the strings */
522 RtlInitEmptyUnicodeString(&DynamicString, NULL, 0);
523 RtlInitEmptyUnicodeString(&RawDllName, NULL, 0);
524 RedirectName = *DllName;
525 pRedirectName = &RedirectName;
526
527 /* Initialize state */
528 RedirectedDll = Locked = FALSE;
529 LdrEntry = NULL;
530 Cookie = 0;
531
532 /* Clear the handle */
533 if (DllHandle) *DllHandle = NULL;
534
535 /* Check for a valid flag combination */
536 if ((Flags & ~(LDR_GET_DLL_HANDLE_EX_PIN | LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT)) ||
537 (!DllHandle && !(Flags & LDR_GET_DLL_HANDLE_EX_PIN)))
538 {
539 DPRINT1("Flags are invalid or no DllHandle given\n");
540 Status = STATUS_INVALID_PARAMETER;
541 goto Quickie;
542 }
543
544 /* If not initializing */
545 if (!LdrpInLdrInit)
546 {
547 /* Acquire the lock */
548 Status = LdrLockLoaderLock(0, NULL, &Cookie);
549 if (!NT_SUCCESS(Status)) goto Quickie;
550
551 /* Remember we own it */
552 Locked = TRUE;
553 }
554
555 Status = LdrpApplyFileNameRedirection(
556 pRedirectName, &LdrApiDefaultExtension, NULL, &DynamicString, &pRedirectName, &RedirectedDll);
557 if (!NT_SUCCESS(Status))
558 {
559 DPRINT1("LdrpApplyFileNameRedirection FAILED: (Status 0x%x)\n", Status);
560 goto Quickie;
561 }
562
563 /* Set default failure code */
564 Status = STATUS_DLL_NOT_FOUND;
565
566 /* Use the cache if we can */
567 if (LdrpGetModuleHandleCache)
568 {
569 /* Check if we were redirected */
570 if (RedirectedDll)
571 {
572 /* Check the flag */
573 if (!(LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED))
574 {
575 goto DontCompare;
576 }
577
578 /* Use the right name */
579 CompareName = &LdrpGetModuleHandleCache->FullDllName;
580 }
581 else
582 {
583 /* Check the flag */
584 if (LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED)
585 {
586 goto DontCompare;
587 }
588
589 /* Use the right name */
590 CompareName = &LdrpGetModuleHandleCache->BaseDllName;
591 }
592
593 /* Check if the name matches */
594 if (RtlEqualUnicodeString(pRedirectName,
595 CompareName,
596 TRUE))
597 {
598 /* Skip the rest */
599 LdrEntry = LdrpGetModuleHandleCache;
600
601 /* Return success */
602 Status = STATUS_SUCCESS;
603 goto Quickie;
604 }
605 }
606
607 DontCompare:
608 /* Find the name without the extension */
609 p1 = pRedirectName->Buffer;
610 p2 = NULL;
611 p3 = &p1[pRedirectName->Length / sizeof(WCHAR)];
612 while (p1 != p3)
613 {
614 if (*p1++ == L'.')
615 {
616 p2 = p1;
617 }
618 else if (*p1 == L'\\')
619 {
620 p2 = NULL;
621 }
622 }
623
624 /* Check if no extension was found or if we got a slash */
625 if (!(p2) || (*p2 == L'\\') || (*p2 == L'/'))
626 {
627 /* Check that we have space to add one */
628 Length = pRedirectName->Length +
629 LdrApiDefaultExtension.Length + sizeof(UNICODE_NULL);
630 if (Length >= UNICODE_STRING_MAX_BYTES)
631 {
632 /* No space to add the extension */
633 Status = STATUS_NAME_TOO_LONG;
634 goto Quickie;
635 }
636
637 /* Setup the string */
638 RawDllName.MaximumLength = Length;
639 ASSERT(Length >= sizeof(UNICODE_NULL));
640 RawDllName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
641 0,
642 RawDllName.MaximumLength);
643 if (!RawDllName.Buffer)
644 {
645 Status = STATUS_NO_MEMORY;
646 goto Quickie;
647 }
648
649 /* Copy the string and add extension */
650 RtlCopyUnicodeString(&RawDllName, pRedirectName);
651 RtlAppendUnicodeStringToString(&RawDllName, &LdrApiDefaultExtension);
652 }
653 else
654 {
655 /* Check if there's something in the name */
656 Length = pRedirectName->Length;
657 if (Length)
658 {
659 /* Check and remove trailing period */
660 if (pRedirectName->Buffer[Length / sizeof(WCHAR) - sizeof(ANSI_NULL)] == '.')
661 {
662 /* Decrease the size */
663 pRedirectName->Length -= sizeof(WCHAR);
664 }
665 }
666
667 /* Setup the string */
668 RawDllName.MaximumLength = pRedirectName->Length + sizeof(WCHAR);
669 RawDllName.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
670 0,
671 RawDllName.MaximumLength);
672 if (!RawDllName.Buffer)
673 {
674 Status = STATUS_NO_MEMORY;
675 goto Quickie;
676 }
677
678 /* Copy the string */
679 RtlCopyUnicodeString(&RawDllName, pRedirectName);
680 }
681
682 /* Display debug string */
683 if (ShowSnaps)
684 {
685 DPRINT1("LDR: LdrGetDllHandleEx, searching for %wZ from %ws\n",
686 &RawDllName,
687 DllPath ? ((ULONG_PTR)DllPath == 1 ? L"" : DllPath) : L"");
688 }
689
690 /* Do the lookup */
691 if (LdrpCheckForLoadedDll(DllPath,
692 &RawDllName,
693 ((ULONG_PTR)DllPath == 1) ? TRUE : FALSE,
694 RedirectedDll,
695 &LdrEntry))
696 {
697 /* Update cached entry */
698 LdrpGetModuleHandleCache = LdrEntry;
699
700 /* Return success */
701 Status = STATUS_SUCCESS;
702 }
703 else
704 {
705 /* Make sure to NULL this */
706 LdrEntry = NULL;
707 }
708 Quickie:
709 /* The success path must have a valid loader entry */
710 ASSERT((LdrEntry != NULL) == NT_SUCCESS(Status));
711
712 /* Check if we got an entry and success */
713 DPRINT("Got LdrEntry->BaseDllName %wZ\n", LdrEntry ? &LdrEntry->BaseDllName : NULL);
714 if ((LdrEntry) && (NT_SUCCESS(Status)))
715 {
716 /* Check if the DLL is locked */
717 if ((LdrEntry->LoadCount != 0xFFFF) &&
718 !(Flags & LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT))
719 {
720 /* Check what to do with the load count */
721 if (Flags & LDR_GET_DLL_HANDLE_EX_PIN)
722 {
723 /* Pin it */
724 LdrEntry->LoadCount = 0xFFFF;
725 LoadFlag = LDRP_UPDATE_PIN;
726 }
727 else
728 {
729 /* Increase the load count */
730 LdrEntry->LoadCount++;
731 LoadFlag = LDRP_UPDATE_REFCOUNT;
732 }
733
734 /* Update the load count now */
735 LdrpUpdateLoadCount2(LdrEntry, LoadFlag);
736 LdrpClearLoadInProgress();
737 }
738
739 /* Check if the caller is requesting the handle */
740 if (DllHandle) *DllHandle = LdrEntry->DllBase;
741 }
742
743 /* Free string if needed */
744 if (DynamicString.Buffer) RtlFreeUnicodeString(&DynamicString);
745
746 /* Free the raw DLL Name if needed */
747 if (RawDllName.Buffer)
748 {
749 /* Free the heap-allocated buffer */
750 RtlFreeHeap(RtlGetProcessHeap(), 0, RawDllName.Buffer);
751 RawDllName.Buffer = NULL;
752 }
753
754 /* Release lock */
755 if (Locked)
756 {
757 LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS,
758 Cookie);
759 }
760
761 /* Return */
762 return Status;
763 }
764
765 /*
766 * @implemented
767 */
768 NTSTATUS
769 NTAPI
LdrGetDllHandle(_In_opt_ PWSTR DllPath,_In_opt_ PULONG DllCharacteristics,_In_ PUNICODE_STRING DllName,_Out_ PVOID * DllHandle)770 LdrGetDllHandle(
771 _In_opt_ PWSTR DllPath,
772 _In_opt_ PULONG DllCharacteristics,
773 _In_ PUNICODE_STRING DllName,
774 _Out_ PVOID *DllHandle)
775 {
776 /* Call the newer API */
777 return LdrGetDllHandleEx(LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT,
778 DllPath,
779 DllCharacteristics,
780 DllName,
781 DllHandle);
782 }
783
784 /*
785 * @implemented
786 */
787 NTSTATUS
788 NTAPI
789 LdrGetProcedureAddress(
790 _In_ PVOID BaseAddress,
791 _In_opt_ _When_(Ordinal == 0, _Notnull_) PANSI_STRING Name,
792 _In_opt_ _When_(Name == NULL, _In_range_(>, 0)) ULONG Ordinal,
793 _Out_ PVOID *ProcedureAddress)
794 {
795 /* Call the internal routine and tell it to execute DllInit */
796 return LdrpGetProcedureAddress(BaseAddress, Name, Ordinal, ProcedureAddress, TRUE);
797 }
798
799 /*
800 * @implemented
801 */
802 NTSTATUS
803 NTAPI
LdrVerifyImageMatchesChecksum(_In_ HANDLE FileHandle,_In_ PLDR_CALLBACK Callback,_In_ PVOID CallbackContext,_Out_ PUSHORT ImageCharacteristics)804 LdrVerifyImageMatchesChecksum(
805 _In_ HANDLE FileHandle,
806 _In_ PLDR_CALLBACK Callback,
807 _In_ PVOID CallbackContext,
808 _Out_ PUSHORT ImageCharacteristics)
809 {
810 FILE_STANDARD_INFORMATION FileStandardInfo;
811 PIMAGE_IMPORT_DESCRIPTOR ImportData;
812 PIMAGE_SECTION_HEADER LastSection = NULL;
813 IO_STATUS_BLOCK IoStatusBlock;
814 PIMAGE_NT_HEADERS NtHeader;
815 HANDLE SectionHandle;
816 SIZE_T ViewSize;
817 PVOID ViewBase;
818 BOOLEAN Result, NoActualCheck;
819 NTSTATUS Status;
820 PVOID ImportName;
821 ULONG Size;
822 DPRINT("LdrVerifyImageMatchesChecksum() called\n");
823
824 /* If the handle has the magic KnownDll flag, skip actual checksums */
825 NoActualCheck = ((ULONG_PTR)FileHandle & 1);
826
827 /* Create the section */
828 Status = NtCreateSection(&SectionHandle,
829 SECTION_MAP_EXECUTE,
830 NULL,
831 NULL,
832 PAGE_EXECUTE,
833 SEC_COMMIT,
834 FileHandle);
835 if (!NT_SUCCESS(Status))
836 {
837 DPRINT1 ("NtCreateSection() failed (Status 0x%x)\n", Status);
838 return Status;
839 }
840
841 /* Map the section */
842 ViewSize = 0;
843 ViewBase = NULL;
844 Status = NtMapViewOfSection(SectionHandle,
845 NtCurrentProcess(),
846 &ViewBase,
847 0,
848 0,
849 NULL,
850 &ViewSize,
851 ViewShare,
852 0,
853 PAGE_EXECUTE);
854 if (!NT_SUCCESS(Status))
855 {
856 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
857 NtClose(SectionHandle);
858 return Status;
859 }
860
861 /* Get the file information */
862 Status = NtQueryInformationFile(FileHandle,
863 &IoStatusBlock,
864 &FileStandardInfo,
865 sizeof(FILE_STANDARD_INFORMATION),
866 FileStandardInformation);
867 if (!NT_SUCCESS(Status))
868 {
869 DPRINT1("NtMapViewOfSection() failed (Status 0x%x)\n", Status);
870 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
871 NtClose(SectionHandle);
872 return Status;
873 }
874
875 /* Protect with SEH */
876 _SEH2_TRY
877 {
878 /* Check if this is the KnownDll hack */
879 if (NoActualCheck)
880 {
881 /* Don't actually do it */
882 Result = TRUE;
883 }
884 else
885 {
886 /* Verify the checksum */
887 Result = LdrVerifyMappedImageMatchesChecksum(ViewBase,
888 FileStandardInfo.EndOfFile.LowPart,
889 FileStandardInfo.EndOfFile.LowPart);
890 }
891
892 /* Check if a callback was supplied */
893 if ((Result) && (Callback))
894 {
895 /* Get the NT Header */
896 NtHeader = RtlImageNtHeader(ViewBase);
897
898 /* Check if caller requested this back */
899 if (ImageCharacteristics)
900 {
901 /* Return to caller */
902 *ImageCharacteristics = NtHeader->FileHeader.Characteristics;
903 }
904
905 /* Get the Import Directory Data */
906 ImportData = RtlImageDirectoryEntryToData(ViewBase,
907 FALSE,
908 IMAGE_DIRECTORY_ENTRY_IMPORT,
909 &Size);
910
911 /* Make sure there is one */
912 if (ImportData)
913 {
914 /* Loop the imports */
915 while (ImportData->Name)
916 {
917 /* Get the name */
918 ImportName = RtlImageRvaToVa(NtHeader,
919 ViewBase,
920 ImportData->Name,
921 &LastSection);
922
923 /* Notify the callback */
924 Callback(CallbackContext, ImportName);
925 ImportData++;
926 }
927 }
928 }
929 }
930 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
931 {
932 /* Fail the request returning STATUS_IMAGE_CHECKSUM_MISMATCH */
933 Result = FALSE;
934 }
935 _SEH2_END;
936
937 /* Unmap file and close handle */
938 NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
939 NtClose(SectionHandle);
940
941 /* Return status */
942 return Result ? Status : STATUS_IMAGE_CHECKSUM_MISMATCH;
943 }
944
945 NTSTATUS
946 NTAPI
LdrQueryProcessModuleInformationEx(_In_opt_ ULONG ProcessId,_Reserved_ ULONG Reserved,_Out_writes_bytes_to_ (Size,* ReturnedSize)PRTL_PROCESS_MODULES ModuleInformation,_In_ ULONG Size,_Out_opt_ PULONG ReturnedSize)947 LdrQueryProcessModuleInformationEx(
948 _In_opt_ ULONG ProcessId,
949 _Reserved_ ULONG Reserved,
950 _Out_writes_bytes_to_(Size, *ReturnedSize) PRTL_PROCESS_MODULES ModuleInformation,
951 _In_ ULONG Size,
952 _Out_opt_ PULONG ReturnedSize)
953 {
954 PLIST_ENTRY ModuleListHead, InitListHead;
955 PLIST_ENTRY Entry, InitEntry;
956 PLDR_DATA_TABLE_ENTRY Module, InitModule;
957 PRTL_PROCESS_MODULE_INFORMATION ModulePtr = NULL;
958 NTSTATUS Status = STATUS_SUCCESS;
959 ULONG UsedSize = FIELD_OFFSET(RTL_PROCESS_MODULES, Modules);
960 ANSI_STRING AnsiString;
961 PCHAR p;
962
963 DPRINT("LdrQueryProcessModuleInformation() called\n");
964
965 /* Acquire loader lock */
966 RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock);
967
968 _SEH2_TRY
969 {
970 /* Check if we were given enough space */
971 if (Size < UsedSize)
972 {
973 Status = STATUS_INFO_LENGTH_MISMATCH;
974 }
975 else
976 {
977 ModuleInformation->NumberOfModules = 0;
978 ModulePtr = &ModuleInformation->Modules[0];
979 Status = STATUS_SUCCESS;
980 }
981
982 /* Traverse the list of modules */
983 ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
984 Entry = ModuleListHead->Flink;
985
986 while (Entry != ModuleListHead)
987 {
988 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
989
990 DPRINT(" Module %wZ\n", &Module->FullDllName);
991
992 /* Increase the used size */
993 UsedSize += sizeof(RTL_PROCESS_MODULE_INFORMATION);
994
995 if (UsedSize > Size)
996 {
997 Status = STATUS_INFO_LENGTH_MISMATCH;
998 }
999 else
1000 {
1001 ModulePtr->ImageBase = Module->DllBase;
1002 ModulePtr->ImageSize = Module->SizeOfImage;
1003 ModulePtr->Flags = Module->Flags;
1004 ModulePtr->LoadCount = Module->LoadCount;
1005 ModulePtr->MappedBase = NULL;
1006 ModulePtr->InitOrderIndex = 0;
1007 ModulePtr->LoadOrderIndex = ModuleInformation->NumberOfModules;
1008
1009 /* Now get init order index by traversing init list */
1010 InitListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
1011 InitEntry = InitListHead->Flink;
1012
1013 while (InitEntry != InitListHead)
1014 {
1015 InitModule = CONTAINING_RECORD(InitEntry, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
1016
1017 /* Increase the index */
1018 ModulePtr->InitOrderIndex++;
1019
1020 /* Quit the loop if our module is found */
1021 if (InitModule == Module) break;
1022
1023 /* Advance to the next entry */
1024 InitEntry = InitEntry->Flink;
1025 }
1026
1027 /* Prepare ANSI string with the module's name */
1028 AnsiString.Length = 0;
1029 AnsiString.MaximumLength = sizeof(ModulePtr->FullPathName);
1030 AnsiString.Buffer = ModulePtr->FullPathName;
1031 RtlUnicodeStringToAnsiString(&AnsiString,
1032 &Module->FullDllName,
1033 FALSE);
1034
1035 /* Calculate OffsetToFileName field */
1036 p = strrchr(ModulePtr->FullPathName, '\\');
1037 if (p != NULL)
1038 ModulePtr->OffsetToFileName = p - ModulePtr->FullPathName + 1;
1039 else
1040 ModulePtr->OffsetToFileName = 0;
1041
1042 /* Advance to the next module in the output list */
1043 ModulePtr++;
1044
1045 /* Increase number of modules */
1046 if (ModuleInformation)
1047 ModuleInformation->NumberOfModules++;
1048 }
1049
1050 /* Go to the next entry in the modules list */
1051 Entry = Entry->Flink;
1052 }
1053
1054 /* Set returned size if it was provided */
1055 if (ReturnedSize)
1056 *ReturnedSize = UsedSize;
1057 }
1058 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1059 {
1060 /* Ignoring the exception */
1061 } _SEH2_END;
1062
1063 /* Release the lock */
1064 RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock);
1065
1066 DPRINT("LdrQueryProcessModuleInformation() done\n");
1067
1068 return Status;
1069 }
1070
1071 /*
1072 * @implemented
1073 */
1074 NTSTATUS
1075 NTAPI
LdrQueryProcessModuleInformation(_Out_writes_bytes_to_ (Size,* ReturnedSize)PRTL_PROCESS_MODULES ModuleInformation,_In_ ULONG Size,_Out_opt_ PULONG ReturnedSize)1076 LdrQueryProcessModuleInformation(
1077 _Out_writes_bytes_to_(Size, *ReturnedSize) PRTL_PROCESS_MODULES ModuleInformation,
1078 _In_ ULONG Size,
1079 _Out_opt_ PULONG ReturnedSize)
1080 {
1081 /* Call Ex version of the API */
1082 return LdrQueryProcessModuleInformationEx(0, 0, ModuleInformation, Size, ReturnedSize);
1083 }
1084
1085 /*
1086 * @implemented
1087 */
1088 NTSTATUS
1089 NTAPI
LdrEnumerateLoadedModules(_Reserved_ ULONG ReservedFlag,_In_ PLDR_ENUM_CALLBACK EnumProc,_In_opt_ PVOID Context)1090 LdrEnumerateLoadedModules(
1091 _Reserved_ ULONG ReservedFlag,
1092 _In_ PLDR_ENUM_CALLBACK EnumProc,
1093 _In_opt_ PVOID Context)
1094 {
1095 PLIST_ENTRY ListHead, ListEntry;
1096 PLDR_DATA_TABLE_ENTRY LdrEntry;
1097 NTSTATUS Status;
1098 ULONG_PTR Cookie;
1099 BOOLEAN Stop = FALSE;
1100
1101 /* Check parameters */
1102 if ((ReservedFlag) || !(EnumProc)) return STATUS_INVALID_PARAMETER;
1103
1104 /* Acquire the loader lock */
1105 Status = LdrLockLoaderLock(0, NULL, &Cookie);
1106 if (!NT_SUCCESS(Status)) return Status;
1107
1108 /* Loop all the modules and call enum proc */
1109 ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
1110 ListEntry = ListHead->Flink;
1111 while (ListHead != ListEntry)
1112 {
1113 /* Get the entry */
1114 LdrEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
1115
1116 /* Call the enumeration proc inside SEH */
1117 _SEH2_TRY
1118 {
1119 EnumProc(LdrEntry, Context, &Stop);
1120 }
1121 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1122 {
1123 /* Ignoring the exception */
1124 } _SEH2_END;
1125
1126 /* Break if we were asked to stop enumeration */
1127 if (Stop)
1128 {
1129 break;
1130 }
1131
1132 /* Advance to the next module */
1133 ListEntry = ListEntry->Flink;
1134 }
1135
1136 /* Release loader lock */
1137 Status = LdrUnlockLoaderLock(0, Cookie);
1138 ASSERT(NT_SUCCESS(Status));
1139
1140 /* Reset any successful status to STATUS_SUCCESS,
1141 * but leave failure to the caller */
1142 if (NT_SUCCESS(Status))
1143 Status = STATUS_SUCCESS;
1144
1145 /* Return any possible failure status */
1146 return Status;
1147 }
1148
1149 /*
1150 * @implemented
1151 */
1152 NTSTATUS
1153 NTAPI
LdrDisableThreadCalloutsForDll(_In_ PVOID BaseAddress)1154 LdrDisableThreadCalloutsForDll(
1155 _In_ PVOID BaseAddress)
1156 {
1157 PLDR_DATA_TABLE_ENTRY LdrEntry;
1158 NTSTATUS Status;
1159 BOOLEAN LockHeld;
1160 ULONG_PTR Cookie;
1161 DPRINT("LdrDisableThreadCalloutsForDll (BaseAddress %p)\n", BaseAddress);
1162
1163 /* Don't do it during shutdown */
1164 if (LdrpShutdownInProgress) return STATUS_SUCCESS;
1165
1166 /* Check if we should grab the lock */
1167 LockHeld = FALSE;
1168 if (!LdrpInLdrInit)
1169 {
1170 /* Grab the lock */
1171 Status = LdrLockLoaderLock(0, NULL, &Cookie);
1172 if (!NT_SUCCESS(Status)) return Status;
1173 LockHeld = TRUE;
1174 }
1175
1176 /* Make sure the DLL is valid and get its entry */
1177 Status = STATUS_DLL_NOT_FOUND;
1178 if (LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1179 {
1180 /* Get if it has a TLS slot */
1181 if (!LdrEntry->TlsIndex)
1182 {
1183 /* It doesn't, so you're allowed to call this */
1184 LdrEntry->Flags |= LDRP_DONT_CALL_FOR_THREADS;
1185 Status = STATUS_SUCCESS;
1186 }
1187 }
1188
1189 /* Check if the lock was held */
1190 if (LockHeld)
1191 {
1192 /* Release it */
1193 LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
1194 }
1195
1196 /* Return the status */
1197 return Status;
1198 }
1199
1200 /*
1201 * @implemented
1202 */
1203 NTSTATUS
1204 NTAPI
LdrAddRefDll(_In_ ULONG Flags,_In_ PVOID BaseAddress)1205 LdrAddRefDll(
1206 _In_ ULONG Flags,
1207 _In_ PVOID BaseAddress)
1208 {
1209 PLDR_DATA_TABLE_ENTRY LdrEntry;
1210 NTSTATUS Status = STATUS_SUCCESS;
1211 ULONG_PTR Cookie;
1212 BOOLEAN Locked = FALSE;
1213
1214 /* Check for invalid flags */
1215 if (Flags & ~(LDR_ADDREF_DLL_PIN))
1216 {
1217 /* Fail with invalid parameter status if so */
1218 Status = STATUS_INVALID_PARAMETER;
1219 goto quickie;
1220 }
1221
1222 /* Acquire the loader lock if not in init phase */
1223 if (!LdrpInLdrInit)
1224 {
1225 /* Acquire the lock */
1226 Status = LdrLockLoaderLock(0, NULL, &Cookie);
1227 if (!NT_SUCCESS(Status)) goto quickie;
1228 Locked = TRUE;
1229 }
1230
1231 /* Get this module's data table entry */
1232 if (LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1233 {
1234 if (!LdrEntry)
1235 {
1236 /* Shouldn't happen */
1237 Status = STATUS_INTERNAL_ERROR;
1238 goto quickie;
1239 }
1240
1241 /* If this is not a pinned module */
1242 if (LdrEntry->LoadCount != 0xFFFF)
1243 {
1244 /* Update its load count */
1245 if (Flags & LDR_ADDREF_DLL_PIN)
1246 {
1247 /* Pin it by setting load count to -1 */
1248 LdrEntry->LoadCount = 0xFFFF;
1249 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_PIN);
1250 }
1251 else
1252 {
1253 /* Increase its load count by one */
1254 LdrEntry->LoadCount++;
1255 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_REFCOUNT);
1256 }
1257
1258 /* Clear load in progress */
1259 LdrpClearLoadInProgress();
1260 }
1261 }
1262 else
1263 {
1264 /* There was an error getting this module's handle, return invalid param status */
1265 Status = STATUS_INVALID_PARAMETER;
1266 }
1267
1268 quickie:
1269 /* Check for error case */
1270 if (!NT_SUCCESS(Status))
1271 {
1272 /* Print debug information */
1273 if ((ShowSnaps) || ((Status != STATUS_NO_SUCH_FILE) &&
1274 (Status != STATUS_DLL_NOT_FOUND) &&
1275 (Status != STATUS_OBJECT_NAME_NOT_FOUND)))
1276 {
1277 DPRINT1("LDR: LdrAddRefDll(%p) 0x%08lx\n", BaseAddress, Status);
1278 }
1279 }
1280
1281 /* Release the lock if needed */
1282 if (Locked) LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
1283 return Status;
1284 }
1285
1286 /*
1287 * @implemented
1288 */
1289 NTSTATUS
1290 NTAPI
LdrUnloadDll(_In_ PVOID BaseAddress)1291 LdrUnloadDll(
1292 _In_ PVOID BaseAddress)
1293 {
1294 NTSTATUS Status = STATUS_SUCCESS;
1295 PPEB Peb = NtCurrentPeb();
1296 PLDR_DATA_TABLE_ENTRY LdrEntry, CurrentEntry;
1297 PVOID EntryPoint;
1298 PLIST_ENTRY NextEntry;
1299 LIST_ENTRY UnloadList;
1300 RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED ActCtx;
1301 PVOID CorImageData;
1302 ULONG ComSectionSize;
1303
1304 /* Get the LDR Lock */
1305 if (!LdrpInLdrInit) RtlEnterCriticalSection(Peb->LoaderLock);
1306
1307 /* Increase the unload count */
1308 LdrpActiveUnloadCount++;
1309
1310 /* Skip unload */
1311 if (LdrpShutdownInProgress) goto Quickie;
1312
1313 /* Make sure the DLL is valid and get its entry */
1314 if (!LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
1315 {
1316 Status = STATUS_DLL_NOT_FOUND;
1317 goto Quickie;
1318 }
1319
1320 /* Check the current Load Count */
1321 if (LdrEntry->LoadCount != 0xFFFF)
1322 {
1323 /* Decrease it */
1324 LdrEntry->LoadCount--;
1325
1326 /* If it's a dll */
1327 if (LdrEntry->Flags & LDRP_IMAGE_DLL)
1328 {
1329 /* Set up the Act Ctx */
1330 ActCtx.Size = sizeof(ActCtx);
1331 ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
1332 RtlZeroMemory(&ActCtx.Frame, sizeof(RTL_ACTIVATION_CONTEXT_STACK_FRAME));
1333
1334 /* Activate the ActCtx */
1335 RtlActivateActivationContextUnsafeFast(&ActCtx,
1336 LdrEntry->EntryPointActivationContext);
1337
1338 /* Update the load count */
1339 LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_DEREFCOUNT);
1340
1341 /* Release the context */
1342 RtlDeactivateActivationContextUnsafeFast(&ActCtx);
1343 }
1344 }
1345 else
1346 {
1347 /* The DLL is locked */
1348 goto Quickie;
1349 }
1350
1351 /* Show debug message */
1352 if (ShowSnaps) DPRINT1("LDR: UNINIT LIST\n");
1353
1354 /* Check if this is our only unload and initialize the list if so */
1355 if (LdrpActiveUnloadCount == 1) InitializeListHead(&LdrpUnloadHead);
1356
1357 /* Loop the modules to build the list */
1358 NextEntry = Peb->Ldr->InInitializationOrderModuleList.Blink;
1359 while (NextEntry != &Peb->Ldr->InInitializationOrderModuleList)
1360 {
1361 /* Get the entry */
1362 LdrEntry = CONTAINING_RECORD(NextEntry,
1363 LDR_DATA_TABLE_ENTRY,
1364 InInitializationOrderLinks);
1365 NextEntry = NextEntry->Blink;
1366
1367 /* Remove flag */
1368 LdrEntry->Flags &= ~LDRP_UNLOAD_IN_PROGRESS;
1369
1370 /* If the load count is now 0 */
1371 if (!LdrEntry->LoadCount)
1372 {
1373 /* Show message */
1374 if (ShowSnaps)
1375 {
1376 DPRINT1("(%lu) [%ws] %ws (%lx) deinit %p\n",
1377 LdrpActiveUnloadCount,
1378 LdrEntry->BaseDllName.Buffer,
1379 LdrEntry->FullDllName.Buffer,
1380 (ULONG)LdrEntry->LoadCount,
1381 LdrEntry->EntryPoint);
1382 }
1383
1384 /* Call Shim Engine and notify */
1385 if (g_ShimsEnabled)
1386 {
1387 VOID (NTAPI* SE_DllUnloaded)(PVOID) = RtlDecodeSystemPointer(g_pfnSE_DllUnloaded);
1388 SE_DllUnloaded(LdrEntry);
1389 }
1390
1391 /* Unlink it */
1392 CurrentEntry = LdrEntry;
1393 RemoveEntryList(&CurrentEntry->InInitializationOrderLinks);
1394 RemoveEntryList(&CurrentEntry->InMemoryOrderLinks);
1395 RemoveEntryList(&CurrentEntry->HashLinks);
1396
1397 /* If there's more then one active unload */
1398 if (LdrpActiveUnloadCount > 1)
1399 {
1400 /* Flush the cached DLL handle and clear the list */
1401 LdrpLoadedDllHandleCache = NULL;
1402 CurrentEntry->InMemoryOrderLinks.Flink = NULL;
1403 }
1404
1405 /* Add the entry on the unload list */
1406 InsertTailList(&LdrpUnloadHead, &CurrentEntry->HashLinks);
1407 }
1408 }
1409
1410 /* Only call the entrypoints once */
1411 if (LdrpActiveUnloadCount > 1) goto Quickie;
1412
1413 /* Now loop the unload list and create our own */
1414 InitializeListHead(&UnloadList);
1415 CurrentEntry = NULL;
1416 NextEntry = LdrpUnloadHead.Flink;
1417 while (NextEntry != &LdrpUnloadHead)
1418 {
1419 /* Get the current entry */
1420 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
1421
1422 LdrpRecordUnloadEvent(LdrEntry);
1423
1424 /* Set the entry and clear it from the list */
1425 CurrentEntry = LdrEntry;
1426 LdrpLoadedDllHandleCache = NULL;
1427 CurrentEntry->InMemoryOrderLinks.Flink = NULL;
1428
1429 /* Move it from the global to the local list */
1430 RemoveEntryList(&CurrentEntry->HashLinks);
1431 InsertTailList(&UnloadList, &CurrentEntry->HashLinks);
1432
1433 /* Get the entrypoint */
1434 EntryPoint = LdrEntry->EntryPoint;
1435
1436 /* Check if we should call it */
1437 if ((EntryPoint) && (LdrEntry->Flags & LDRP_PROCESS_ATTACH_CALLED))
1438 {
1439 /* Show message */
1440 if (ShowSnaps)
1441 {
1442 DPRINT1("LDR: Calling deinit %p\n", EntryPoint);
1443 }
1444
1445 /* Set up the Act Ctx */
1446 ActCtx.Size = sizeof(ActCtx);
1447 ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
1448 RtlZeroMemory(&ActCtx.Frame, sizeof(RTL_ACTIVATION_CONTEXT_STACK_FRAME));
1449
1450 /* Activate the ActCtx */
1451 RtlActivateActivationContextUnsafeFast(&ActCtx,
1452 LdrEntry->EntryPointActivationContext);
1453
1454 /* Call the entrypoint */
1455 _SEH2_TRY
1456 {
1457 LdrpCallInitRoutine(LdrEntry->EntryPoint,
1458 LdrEntry->DllBase,
1459 DLL_PROCESS_DETACH,
1460 NULL);
1461 }
1462 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1463 {
1464 DPRINT1("WARNING: Exception 0x%x during LdrpCallInitRoutine(DLL_PROCESS_DETACH) for %wZ\n",
1465 _SEH2_GetExceptionCode(), &LdrEntry->BaseDllName);
1466 }
1467 _SEH2_END;
1468
1469 /* Release the context */
1470 RtlDeactivateActivationContextUnsafeFast(&ActCtx);
1471 }
1472
1473 /* Remove it from the list */
1474 RemoveEntryList(&CurrentEntry->InLoadOrderLinks);
1475 CurrentEntry = NULL;
1476 NextEntry = LdrpUnloadHead.Flink;
1477 }
1478
1479 /* Now loop our local list */
1480 NextEntry = UnloadList.Flink;
1481 while (NextEntry != &UnloadList)
1482 {
1483 /* Get the entry */
1484 LdrEntry = CONTAINING_RECORD(NextEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
1485 NextEntry = NextEntry->Flink;
1486 CurrentEntry = LdrEntry;
1487
1488 /* Notify Application Verifier */
1489 if (Peb->NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK)
1490 {
1491 AVrfDllUnloadNotification(LdrEntry);
1492 }
1493
1494 /* Show message */
1495 if (ShowSnaps)
1496 {
1497 DPRINT1("LDR: Unmapping [%ws]\n", LdrEntry->BaseDllName.Buffer);
1498 }
1499
1500 /* Check if this is a .NET executable */
1501 CorImageData = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
1502 TRUE,
1503 IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,
1504 &ComSectionSize);
1505 if (CorImageData)
1506 {
1507 /* FIXME */
1508 DPRINT1(".NET Images are not supported yet\n");
1509 }
1510
1511 /* Check if we should unmap*/
1512 if (!(CurrentEntry->Flags & LDR_COR_OWNS_UNMAP))
1513 {
1514 /* Unmap the DLL */
1515 Status = NtUnmapViewOfSection(NtCurrentProcess(),
1516 CurrentEntry->DllBase);
1517 ASSERT(NT_SUCCESS(Status));
1518 }
1519
1520 /* Unload the alternate resource module, if any */
1521 LdrUnloadAlternateResourceModule(CurrentEntry->DllBase);
1522
1523 /* FIXME: Send shutdown notification */
1524 //LdrpSendDllNotifications(CurrentEntry, 2, LdrpShutdownInProgress);
1525
1526 /* Check if a Hotpatch is active */
1527 if (LdrEntry->PatchInformation)
1528 {
1529 /* FIXME */
1530 DPRINT1("We don't support Hotpatching yet\n");
1531 }
1532
1533 /* Deallocate the Entry */
1534 LdrpFinalizeAndDeallocateDataTableEntry(CurrentEntry);
1535
1536 /* If this is the cached entry, invalidate it */
1537 if (LdrpGetModuleHandleCache == CurrentEntry)
1538 {
1539 LdrpGetModuleHandleCache = NULL;
1540 }
1541 }
1542
1543 Quickie:
1544 /* Decrease unload count */
1545 LdrpActiveUnloadCount--;
1546 if (!LdrpInLdrInit) RtlLeaveCriticalSection(Peb->LoaderLock);
1547
1548 /* Return to caller */
1549 return Status;
1550 }
1551
1552 /*
1553 * @implemented
1554 */
1555 BOOLEAN
1556 NTAPI
RtlDllShutdownInProgress(VOID)1557 RtlDllShutdownInProgress(VOID)
1558 {
1559 /* Return the internal global */
1560 return LdrpShutdownInProgress;
1561 }
1562
1563 /*
1564 * @implemented
1565 */
1566 PIMAGE_BASE_RELOCATION
1567 NTAPI
LdrProcessRelocationBlock(_In_ ULONG_PTR Address,_In_ ULONG Count,_In_ PUSHORT TypeOffset,_In_ LONG_PTR Delta)1568 LdrProcessRelocationBlock(
1569 _In_ ULONG_PTR Address,
1570 _In_ ULONG Count,
1571 _In_ PUSHORT TypeOffset,
1572 _In_ LONG_PTR Delta)
1573 {
1574 return LdrProcessRelocationBlockLongLong(Address, Count, TypeOffset, Delta);
1575 }
1576
1577 /* FIXME: Add to ntstatus.mc */
1578 #define STATUS_MUI_FILE_NOT_FOUND ((NTSTATUS)0xC00B0001L)
1579
1580 /*
1581 * @implemented
1582 */
1583 NTSTATUS
1584 NTAPI
LdrLoadAlternateResourceModule(_In_ PVOID Module,_In_ PWSTR Buffer)1585 LdrLoadAlternateResourceModule(
1586 _In_ PVOID Module,
1587 _In_ PWSTR Buffer)
1588 {
1589 /* Is MUI Support enabled? */
1590 if (!LdrAlternateResourcesEnabled()) return STATUS_SUCCESS;
1591
1592 UNIMPLEMENTED;
1593 return STATUS_MUI_FILE_NOT_FOUND;
1594 }
1595
1596 /*
1597 * @implemented
1598 */
1599 BOOLEAN
1600 NTAPI
LdrUnloadAlternateResourceModule(_In_ PVOID BaseAddress)1601 LdrUnloadAlternateResourceModule(
1602 _In_ PVOID BaseAddress)
1603 {
1604 ULONG_PTR Cookie;
1605
1606 /* Acquire the loader lock */
1607 LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &Cookie);
1608
1609 /* Check if there's any alternate resources loaded */
1610 if (AlternateResourceModuleCount)
1611 {
1612 UNIMPLEMENTED;
1613 }
1614
1615 /* Release the loader lock */
1616 LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, Cookie);
1617
1618 /* All done */
1619 return TRUE;
1620 }
1621
1622 /*
1623 * @unimplemented
1624 */
1625 BOOLEAN
1626 NTAPI
LdrFlushAlternateResourceModules(VOID)1627 LdrFlushAlternateResourceModules(VOID)
1628 {
1629 UNIMPLEMENTED;
1630 return FALSE;
1631 }
1632
1633 /*
1634 * @unimplemented
1635 * See https://www.kernelmode.info/forum/viewtopic.php?t=991
1636 */
1637 NTSTATUS
1638 NTAPI
LdrSetAppCompatDllRedirectionCallback(_In_ ULONG Flags,_In_ PLDR_APP_COMPAT_DLL_REDIRECTION_CALLBACK_FUNCTION CallbackFunction,_In_opt_ PVOID CallbackData)1639 LdrSetAppCompatDllRedirectionCallback(
1640 _In_ ULONG Flags,
1641 _In_ PLDR_APP_COMPAT_DLL_REDIRECTION_CALLBACK_FUNCTION CallbackFunction,
1642 _In_opt_ PVOID CallbackData)
1643 {
1644 UNIMPLEMENTED;
1645 return STATUS_NOT_IMPLEMENTED;
1646 }
1647
1648 BOOLEAN
1649 NTAPI
LdrInitShimEngineDynamic(IN PVOID BaseAddress)1650 LdrInitShimEngineDynamic(IN PVOID BaseAddress)
1651 {
1652 ULONG_PTR Cookie;
1653 NTSTATUS Status = LdrLockLoaderLock(0, NULL, &Cookie);
1654 if (NT_SUCCESS(Status))
1655 {
1656 if (!g_pShimEngineModule)
1657 {
1658 g_pShimEngineModule = BaseAddress;
1659 LdrpGetShimEngineInterface();
1660 }
1661 LdrUnlockLoaderLock(0, Cookie);
1662 return TRUE;
1663 }
1664 return FALSE;
1665 }
1666
1667 /* EOF */
1668