xref: /reactos/dll/ntdll/ldr/ldrapi.c (revision 24a56f89)
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