Windows-Server-2003/base/ntdll/ldrapi.c

3025 lines
86 KiB
C

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
ldrapi.c
Abstract:
This module implements the Ldr APIs that can be linked with
an application to perform loader services. All of the APIs in
this component are implemented in a DLL. They are not part of the
DLL snap procedure.
Author:
Mike O'Leary (mikeol) 23-Mar-1990
Revision History:
--*/
#include "ldrp.h"
#include "ntos.h"
#include "nt.h"
#include "ntrtl.h"
#include "nturtl.h"
#include "objidl.h"
#include <windows.h>
#include <apcompat.h>
#include <shimhapi.h>
#if defined(_WIN64)
#include <wow64t.h>
#endif // defined(_WIN64)
#define ULONG_PTR_IZE(_x) ((ULONG_PTR) (_x))
#define ULONG_PTR_IZE_SHIFT_AND_MASK(_x, _shift, _mask) ((ULONG_PTR) ((ULONG_PTR_IZE((_x)) & (_mask)) << (_shift)))
#define CHAR_BITS 8
#define LOADER_LOCK_COOKIE_TYPE_BIT_LENGTH (4)
#define LOADER_LOCK_COOKIE_TYPE_BIT_OFFSET ((CHAR_BITS * sizeof(PVOID)) - LOADER_LOCK_COOKIE_TYPE_BIT_LENGTH)
#define LOADER_LOCK_COOKIE_TYPE_BIT_MASK ((1 << LOADER_LOCK_COOKIE_TYPE_BIT_LENGTH) - 1)
#define LOADER_LOCK_COOKIE_TID_BIT_LENGTH (12)
#define LOADER_LOCK_COOKIE_TID_BIT_OFFSET (LOADER_LOCK_COOKIE_TYPE_BIT_OFFSET - LOADER_LOCK_COOKIE_TID_BIT_LENGTH)
#define LOADER_LOCK_COOKIE_TID_BIT_MASK ((1 << LOADER_LOCK_COOKIE_TID_BIT_LENGTH) - 1)
#define LOADER_LOCK_COOKIE_CODE_BIT_LENGTH (16)
#define LOADER_LOCK_COOKIE_CODE_BIT_OFFSET (0)
#define LOADER_LOCK_COOKIE_CODE_BIT_MASK ((1 << LOADER_LOCK_COOKIE_CODE_BIT_LENGTH) - 1)
#define MAKE_LOADER_LOCK_COOKIE(_type, _code) \
((ULONG_PTR) (ULONG_PTR_IZE_SHIFT_AND_MASK((_type), LOADER_LOCK_COOKIE_TYPE_BIT_OFFSET, LOADER_LOCK_COOKIE_TYPE_BIT_MASK) | \
ULONG_PTR_IZE_SHIFT_AND_MASK((HandleToUlong((NtCurrentTeb())->ClientId.UniqueThread)), LOADER_LOCK_COOKIE_TID_BIT_OFFSET, LOADER_LOCK_COOKIE_TID_BIT_MASK) | \
ULONG_PTR_IZE_SHIFT_AND_MASK((_code), LOADER_LOCK_COOKIE_CODE_BIT_OFFSET, LOADER_LOCK_COOKIE_CODE_BIT_MASK)))
#define EXTRACT_LOADER_LOCK_COOKIE_FIELD(_cookie, _shift, _mask) ((((ULONG_PTR) (_cookie)) >> (_shift)) & (_mask))
#define EXTRACT_LOADER_LOCK_COOKIE_TYPE(_cookie) EXTRACT_LOADER_LOCK_COOKIE_FIELD((_cookie), LOADER_LOCK_COOKIE_TYPE_BIT_OFFSET, LOADER_LOCK_COOKIE_TYPE_BIT_MASK)
#define EXTRACT_LOADER_LOCK_COOKIE_TID(_cookie) EXTRACT_LOADER_LOCK_COOKIE_FIELD((_cookie), LOADER_LOCK_COOKIE_TID_BIT_OFFSET, LOADER_LOCK_COOKIE_TID_BIT_MASK)
#define LOADER_LOCK_COOKIE_TYPE_NORMAL (0)
LONG LdrpLoaderLockAcquisitionCount;
// Note the case inconsistency is due to preserving case from earlier versions.
WCHAR DllExtension[] = L".dll";
UNICODE_STRING LdrApiDefaultExtension = RTL_CONSTANT_STRING(L".DLL");
PLDR_MANIFEST_PROBER_ROUTINE LdrpManifestProberRoutine = NULL;
extern PFNSE_DLLLOADED g_pfnSE_DllLoaded;
extern PFNSE_DLLUNLOADED g_pfnSE_DllUnloaded;
PLDR_APP_COMPAT_DLL_REDIRECTION_CALLBACK_FUNCTION LdrpAppCompatDllRedirectionCallbackFunction = NULL;
PVOID LdrpAppCompatDllRedirectionCallbackData = NULL;
BOOLEAN LdrpShowRecursiveDllLoads;
BOOLEAN LdrpBreakOnRecursiveDllLoads;
PLDR_DATA_TABLE_ENTRY LdrpCurrentDllInitializer;
VOID
RtlpDphDisableFaultInjection (
);
VOID
RtlpDphEnableFaultInjection (
);
ULONG
LdrpClearLoadInProgress(
VOID
);
NTSTATUS
LdrLoadDll (
IN PCWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN PCUNICODE_STRING DllName,
OUT PVOID *DllHandle
)
/*++
Routine Description:
This function loads a DLL into the calling process address space.
Arguments:
DllPath - Supplies the search path to be used to locate the DLL.
DllCharacteristics - Supplies an optional DLL characteristics flag,
that if specified is used to match against the dll being loaded.
DllName - Supplies the name of the DLL to load.
DllHandle - Returns a handle to the loaded DLL.
Return Value:
NTSTATUS.
--*/
{
NTSTATUS Status;
WCHAR StaticRedirectedDllNameBuffer[DOS_MAX_PATH_LENGTH];
UNICODE_STRING StaticRedirectedDllName;
UNICODE_STRING DynamicRedirectedDllName = {0};
ULONG LoadDllFlags = 0;
PCUNICODE_STRING OldTopLevelDllBeingLoaded = NULL;
PVOID LockCookie = NULL;
PTEB Teb;
//
// We need to disable page heap fault injection while loader is active.
// This is important so that we avoid lots of hits(failures) in this
// area. The Disable/Enable function have basically zero impact on
// performance because they just increment/decrement a lock variable
// that is checked when an actual allocation is performed (page heap
// needs to be enabled for that).
//
RtlpDphDisableFaultInjection ();
StaticRedirectedDllName.Length = 0;
StaticRedirectedDllName.MaximumLength = sizeof(StaticRedirectedDllNameBuffer);
StaticRedirectedDllName.Buffer = StaticRedirectedDllNameBuffer;
Status = RtlDosApplyFileIsolationRedirection_Ustr(
RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL,
DllName, // dll name to look up
&LdrApiDefaultExtension,
&StaticRedirectedDllName,
&DynamicRedirectedDllName,
(PUNICODE_STRING*)&DllName, // Result is either StaticRedirectedDllName or DynamicRedirectedDllName
NULL,
NULL, // not interested in where the filename starts
NULL); // not interested in bytes required if we only had a static string
if (NT_SUCCESS(Status)) {
LoadDllFlags |= LDRP_LOAD_DLL_FLAG_DLL_IS_REDIRECTED;
} else if (Status != STATUS_SXS_KEY_NOT_FOUND) {
#if DBG
DbgPrint("%s(%wZ): RtlDosApplyFileIsolationRedirection_Ustr() failed with status %08lx\n", __FUNCTION__, DllName, Status);
#endif // DBG
goto Exit;
}
LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &LockCookie);
OldTopLevelDllBeingLoaded = LdrpTopLevelDllBeingLoaded;
if (OldTopLevelDllBeingLoaded) {
if (ShowSnaps || LdrpShowRecursiveDllLoads || LdrpBreakOnRecursiveDllLoads) {
Teb = NtCurrentTeb();
DbgPrint(
"[%lx,%lx] LDR: Recursive DLL load\n",
HandleToULong(Teb->ClientId.UniqueProcess),
HandleToULong(Teb->ClientId.UniqueThread));
DbgPrint(
"[%lx,%lx] Previous DLL being loaded: \"%wZ\"\n",
HandleToULong(Teb->ClientId.UniqueProcess),
HandleToULong(Teb->ClientId.UniqueThread),
OldTopLevelDllBeingLoaded);
DbgPrint(
"[%lx,%lx] DLL being requested: \"%wZ\"\n",
HandleToULong(Teb->ClientId.UniqueProcess),
HandleToULong(Teb->ClientId.UniqueThread),
DllName);
if (LdrpCurrentDllInitializer != NULL) {
DbgPrint(
"[%lx,%lx] DLL whose initializer was currently running: \"%wZ\"\n",
HandleToULong(Teb->ClientId.UniqueProcess),
HandleToULong(Teb->ClientId.UniqueThread),
&LdrpCurrentDllInitializer->FullDllName);
} else {
DbgPrint(
"[%lx,%lx] No DLL initializer was running\n",
HandleToULong(Teb->ClientId.UniqueProcess),
HandleToULong(Teb->ClientId.UniqueThread));
}
}
}
LdrpTopLevelDllBeingLoaded = DllName;
__try {
Status = LdrpLoadDll (LoadDllFlags,
DllPath,
DllCharacteristics,
DllName,
DllHandle,
TRUE);
if (!NT_SUCCESS(Status)) {
if ((Status != STATUS_NO_SUCH_FILE) &&
(Status != STATUS_DLL_NOT_FOUND) &&
(Status != STATUS_OBJECT_NAME_NOT_FOUND)) {
// Dll initialization failure is common enough that we won't want to print unless snaps are turned on.
if (ShowSnaps || (Status != STATUS_DLL_INIT_FAILED)) {
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
"LDR: %s - failing because LdrpLoadDll(%wZ) returned status %x\n",
__FUNCTION__,
DllName,
Status);
}
}
__leave;
}
Status = STATUS_SUCCESS;
} __finally {
LdrpTopLevelDllBeingLoaded = OldTopLevelDllBeingLoaded;
LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
}
Exit:
if (DynamicRedirectedDllName.Buffer != NULL) {
RtlFreeUnicodeString(&DynamicRedirectedDllName);
}
//
// Reenable page heap fault injection.
//
RtlpDphEnableFaultInjection ();
return Status;
}
NTSTATUS
NTAPI
LdrpLoadDll (
IN ULONG Flags OPTIONAL,
IN PCWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN PCUNICODE_STRING DllName,
OUT PVOID *DllHandle,
IN BOOLEAN RunInitRoutines
)
{
NTSTATUS st;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PWSTR ActualDllName;
PWCH p, pp;
UNICODE_STRING ActualDllNameStr;
WCHAR FreeBuffer[LDR_MAX_PATH + 1];
BOOLEAN Redirected;
ULONG DllNameLength;
const InLdrInit = LdrpInLdrInit;
if (Flags & LDRP_LOAD_DLL_FLAG_DLL_IS_REDIRECTED) {
Redirected = TRUE;
}
else {
Redirected = FALSE;
}
st = STATUS_SUCCESS;
p = DllName->Buffer;
pp = NULL;
while (*p) {
switch (*p++) {
case L'.':
//
// pp will point to first character after the last '.', if
// it occurs after the last '\'.
//
pp = p;
break;
case L'\\':
pp = NULL;
break;
default:
NOTHING;
}
}
if (DllName->Length >= sizeof(FreeBuffer)) {
return STATUS_NAME_TOO_LONG;
}
ActualDllName = FreeBuffer;
RtlCopyMemory (ActualDllName, DllName->Buffer, DllName->Length);
if (!pp || *pp == (WCHAR)'\\') {
//
// No extension found (just ..\)
//
DllNameLength = DllName->Length + sizeof(DllExtension) - sizeof(WCHAR);
if ((DllNameLength + sizeof(WCHAR)) >= sizeof(FreeBuffer)) {
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
"LDR: %s - Dll name missing extension; with extension added the length is too long\n"
" DllName: (@ %p) \"%wZ\"\n"
" DllName->Length: %u\n",
__FUNCTION__,
DllName, DllName,
DllName->Length);
return STATUS_NAME_TOO_LONG;
}
RtlCopyMemory ((PCHAR)ActualDllName+DllName->Length, DllExtension, sizeof(DllExtension));
ActualDllNameStr.Length = (USHORT)(DllNameLength);
} else {
ActualDllName[DllName->Length >> 1] = UNICODE_NULL;
ActualDllNameStr.Length = DllName->Length;
}
ActualDllNameStr.MaximumLength = sizeof(FreeBuffer);
ActualDllNameStr.Buffer = ActualDllName;
LdrDataTableEntry = NULL;
//
// Except during process initialization, grab loader lock and
// Snap all links to the specified DLL.
//
if (!InLdrInit) {
RtlEnterCriticalSection (&LdrpLoaderLock);
}
try {
if (ShowSnaps) {
DbgPrint("LDR: LdrLoadDll, loading %ws from %ws\n",
ActualDllName,
ARGUMENT_PRESENT(DllPath) ? DllPath : L""
);
}
if (!LdrpCheckForLoadedDll( DllPath,
&ActualDllNameStr,
FALSE,
Redirected,
&LdrDataTableEntry)) {
st = LdrpMapDll(DllPath,
ActualDllName,
DllCharacteristics,
FALSE,
Redirected,
&LdrDataTableEntry);
if (!NT_SUCCESS(st)) {
leave;
}
#if defined(_X86_)
//
// Register dll with the stack tracing module.
// This is used for getting reliable stack traces on X86.
//
RtlpStkMarkDllRange (LdrDataTableEntry);
#endif
if (ARGUMENT_PRESENT( DllCharacteristics ) &&
*DllCharacteristics & IMAGE_FILE_EXECUTABLE_IMAGE) {
LdrDataTableEntry->EntryPoint = 0;
LdrDataTableEntry->Flags &= ~LDRP_IMAGE_DLL;
}
//
// walk the import descriptor table of the dll
//
if (LdrDataTableEntry->Flags & LDRP_IMAGE_DLL) {
try {
//
// if the image is COR-ILONLY, then don't walk the import descriptor
// as it is assumed that it only imports %windir%\system32\mscoree.dll, otherwise
// walk the import descriptor table of the dll.
//
if ((LdrDataTableEntry->Flags & LDRP_COR_IMAGE) == 0) {
st = LdrpWalkImportDescriptor(
DllPath,
LdrDataTableEntry
);
}
} __except(LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
st = GetExceptionCode();
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
"LDR: %s - Exception %x thrown by LdrpWalkImportDescriptor\n",
__FUNCTION__,
st);
}
if ( LdrDataTableEntry->LoadCount != 0xffff ) {
LdrDataTableEntry->LoadCount += 1;
}
LdrpReferenceLoadedDll (LdrDataTableEntry);
if (!NT_SUCCESS(st)) {
LdrDataTableEntry->EntryPoint = NULL;
InsertTailList(
&PebLdr.InInitializationOrderModuleList,
&LdrDataTableEntry->InInitializationOrderLinks);
LdrpClearLoadInProgress();
if (ShowSnaps) {
DbgPrint("LDR: Unloading %wZ due to error %x walking import descriptors\n", DllName, st);
}
LdrUnloadDll((PVOID)LdrDataTableEntry->DllBase);
leave;
}
}
else {
if ( LdrDataTableEntry->LoadCount != 0xffff ) {
LdrDataTableEntry->LoadCount += 1;
}
}
//
// Add init routine to list
//
InsertTailList(&PebLdr.InInitializationOrderModuleList,
&LdrDataTableEntry->InInitializationOrderLinks);
//
// If the loader data base is not fully setup, this load was because
// of a forwarder in the static load set. Can't run init routines
// yet because the load counts are NOT set
//
if ( RunInitRoutines && LdrpLdrDatabaseIsSetup ) {
//
// Shim engine callback. This is the chance to patch
// dynamically loaded modules.
//
if (g_pfnSE_DllLoaded != NULL) {
(*g_pfnSE_DllLoaded)(LdrDataTableEntry);
}
try {
st = LdrpRunInitializeRoutines (NULL);
if ( !NT_SUCCESS(st) ) {
if (ShowSnaps) {
DbgPrint("LDR: Unloading %wZ because either its init routine or one of its static imports failed; status = 0x%08lx", DllName, st);
}
LdrUnloadDll((PVOID)LdrDataTableEntry->DllBase);
}
}
__except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
st = GetExceptionCode();
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
"LDR: %s - Exception %08lx thrown running initialization routines for %wZ\n",
__FUNCTION__,
st,
&LdrDataTableEntry->FullDllName);
LdrUnloadDll((PVOID)LdrDataTableEntry->DllBase);
leave;
}
}
else {
st = STATUS_SUCCESS;
}
}
else {
//
// Count it and everything that it imports.
//
if ( LdrDataTableEntry->Flags & LDRP_IMAGE_DLL &&
LdrDataTableEntry->LoadCount != 0xffff ) {
LdrDataTableEntry->LoadCount += 1;
LdrpReferenceLoadedDll(LdrDataTableEntry);
//
// Now clear the Load in progress bits
//
LdrpClearLoadInProgress();
}
else {
if ( LdrDataTableEntry->LoadCount != 0xffff ) {
LdrDataTableEntry->LoadCount += 1;
}
}
}
}
__finally {
if (!InLdrInit) {
RtlLeaveCriticalSection(&LdrpLoaderLock);
}
}
if (NT_SUCCESS(st)) {
*DllHandle = (PVOID)LdrDataTableEntry->DllBase;
}
else {
*DllHandle = NULL;
}
return st;
}
NTSTATUS
LdrGetDllHandle(
IN PCWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN PCUNICODE_STRING DllName,
OUT PVOID *DllHandle
)
{
//
// Preserve the old behavior.
//
return LdrGetDllHandleEx (LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT,
DllPath,
DllCharacteristics,
DllName,
DllHandle);
}
NTSTATUS
LdrGetDllHandleEx(
IN ULONG Flags,
IN PCWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN PCUNICODE_STRING ConstDllName,
OUT PVOID *DllHandle OPTIONAL
)
/*++
Routine Description:
This function locates the specified DLL and returns its handle.
Arguments:
Flags - various bits to affect the behavior
default: the returned handle is addrefed
LDR_GET_DLL_HANDLE_EX_PIN - the dll will not be unloaded until
the process exits
LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT - the dll's reference
count is not changed
DllPath - Supplies the search path to be used to locate the DLL.
DllCharacteristics - Supplies an optional DLL characteristics flag,
that if specified is used to match against the dll being loaded.
The currently supported flags are:
IMAGE_FILE_EXECUTABLE_IMAGE - indicates that imported dll
referenced by the DLL being loaded should not be followed.
This corresponds to DONT_RESOLVE_DLL_REFERENCES
IMAGE_FILE_SYSTEM - indicates that the DLL is a known trusted
system component and that WinSafer sandbox checking
should not be performed on the DLL before loading it.
DllName - Supplies the name of the DLL to load.
DllHandle - Returns a handle to the loaded DLL.
Return Value:
NTSTATUS.
--*/
{
NTSTATUS st = STATUS_ACCESS_VIOLATION;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry = NULL;
PWCH p, pp, pEnd;
UNICODE_STRING ActualDllNameStr = {0, 0, NULL};
UNICODE_STRING DynamicRedirectedDllName = {0, 0, NULL};
BOOLEAN Redirected = FALSE;
BOOLEAN HoldingLoaderLock = FALSE;
const BOOLEAN InLdrInit = LdrpInLdrInit;
PVOID LockCookie = NULL;
const ULONG ValidFlags = LDR_GET_DLL_HANDLE_EX_PIN | LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT;
UNICODE_STRING xDllName;
const PUNICODE_STRING DllName = &xDllName;
UNREFERENCED_PARAMETER (DllCharacteristics);
xDllName = *ConstDllName;
__try {
if (DllHandle != NULL) {
*DllHandle = NULL;
}
if (Flags & ~ValidFlags) {
st = STATUS_INVALID_PARAMETER;
goto Exit;
}
//
// DllHandle is optional if you are pinning the .dll, otherwise it is mandatory.
//
if ((DllHandle == NULL) &&
(Flags & LDR_GET_DLL_HANDLE_EX_PIN) == 0) {
st = STATUS_INVALID_PARAMETER;
goto Exit;
}
if ((Flags & LDR_GET_DLL_HANDLE_EX_PIN) &&
(Flags & LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT)) {
st = STATUS_INVALID_PARAMETER;
goto Exit;
}
//
// Grab Ldr lock
//
if (!InLdrInit) {
st = LdrLockLoaderLock(0, NULL, &LockCookie);
if (!NT_SUCCESS(st)) {
goto Exit;
}
HoldingLoaderLock = TRUE;
}
st = RtlDosApplyFileIsolationRedirection_Ustr(
RTL_DOS_APPLY_FILE_REDIRECTION_USTR_FLAG_RESPECT_DOT_LOCAL,
DllName,
&LdrApiDefaultExtension,
NULL,
&DynamicRedirectedDllName,
(PUNICODE_STRING*)&DllName,
NULL,
NULL,
NULL);
if (NT_SUCCESS(st)) {
Redirected = TRUE;
} else if (st != STATUS_SXS_KEY_NOT_FOUND) {
// Something unusual and bad happened.
__leave;
}
st = STATUS_DLL_NOT_FOUND;
if ( LdrpGetModuleHandleCache ) {
if (Redirected) {
if (((LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED) != 0) &&
RtlEqualUnicodeString(DllName, &LdrpGetModuleHandleCache->FullDllName, TRUE)) {
LdrDataTableEntry = LdrpGetModuleHandleCache;
st = STATUS_SUCCESS;
goto Exit;
}
} else {
// Not redirected...
if (((LdrpGetModuleHandleCache->Flags & LDRP_REDIRECTED) == 0) &&
RtlEqualUnicodeString(DllName, &LdrpGetModuleHandleCache->BaseDllName, TRUE)) {
LdrDataTableEntry = LdrpGetModuleHandleCache;
st = STATUS_SUCCESS;
goto Exit;
}
}
}
p = DllName->Buffer;
pEnd = p + (DllName->Length / sizeof(WCHAR));
pp = NULL;
while (p != pEnd) {
switch (*p++) {
case L'.':
//
// pp will point to the first character after the last
// '.', if it occurs after the last '\'.
//
pp = p;
break;
case L'\\':
pp = NULL;
break;
default:
NOTHING;
}
}
if ((pp == NULL) || (*pp == L'\\') || (*pp == L'/')) {
//
// The max length here must include the null-termination, but the length itself
// should not. NB that sizeof(DllExtension) will include the size for the
// terminating UNICODE_NULL
//
ActualDllNameStr.MaximumLength = DllName->Length + sizeof(DllExtension);
ActualDllNameStr.Length = ActualDllNameStr.MaximumLength - sizeof(WCHAR);
ActualDllNameStr.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, ActualDllNameStr.MaximumLength);
if (ActualDllNameStr.Buffer == NULL) {
st = STATUS_NO_MEMORY;
goto Exit;
}
//
// Copy the name and the default extension onto the string This magically null-terminates,
// as DllExtension includes the unicode null character.
//
RtlCopyMemory(ActualDllNameStr.Buffer, DllName->Buffer, DllName->Length);
RtlCopyMemory(((PCHAR)ActualDllNameStr.Buffer) + DllName->Length, DllExtension, sizeof(DllExtension));
} else {
//
// Trim the trailing dot
//
if ((DllName->Length != 0) && (DllName->Buffer[(DllName->Length / sizeof(WCHAR)) - 1] == L'.')) {
DllName->Length -= sizeof(WCHAR);
}
//
// Size the buffer, allocate - set the max length to include the NULL character
//
ActualDllNameStr.MaximumLength = DllName->Length + sizeof(WCHAR);
ActualDllNameStr.Length = DllName->Length;
ActualDllNameStr.Buffer = RtlAllocateHeap(RtlProcessHeap(), 0, ActualDllNameStr.MaximumLength);
if (ActualDllNameStr.Buffer == NULL) {
st = STATUS_NO_MEMORY;
goto Exit;
}
//
// Copy data into it
//
RtlCopyMemory(ActualDllNameStr.Buffer, DllName->Buffer, DllName->Length);
//
// And null-terminate by hand
//
ActualDllNameStr.Buffer[ActualDllNameStr.Length / sizeof(WCHAR)] = UNICODE_NULL;
}
//
// Check the LdrTable to see if Dll has already been loaded
// into this image.
//
if (ShowSnaps) {
DbgPrint(
"LDR: LdrGetDllHandle, searching for %wZ from %ws\n",
&ActualDllNameStr,
ARGUMENT_PRESENT(DllPath) ? (DllPath == (PWSTR)1 ? L"" : DllPath) : L""
);
}
//
// sort of a hack, but done to speed up GetModuleHandle. kernel32
// now does a two pass call here to avoid computing
// process dll path
//
if (LdrpCheckForLoadedDll(DllPath,
&ActualDllNameStr,
(BOOLEAN)(DllPath == (PWSTR)1 ? TRUE : FALSE),
Redirected,
&LdrDataTableEntry)) {
LdrpGetModuleHandleCache = LdrDataTableEntry;
st = STATUS_SUCCESS;
goto Exit;
}
LdrDataTableEntry = NULL;
RTL_SOFT_ASSERT(st == STATUS_DLL_NOT_FOUND);
Exit:
ASSERT((LdrDataTableEntry != NULL) == NT_SUCCESS(st));
if (LdrDataTableEntry != NULL && NT_SUCCESS(st)) {
//
// It's standard gross procedure to put the check for 0xffff,
// and the updates of the root LoadCount outside the
// call to LdrpUpdateLoadCount..
//
if (LdrDataTableEntry->LoadCount != 0xffff) {
if ((Flags & LDR_GET_DLL_HANDLE_EX_UNCHANGED_REFCOUNT) != 0) {
// nothing
}
else {
if (Flags & LDR_GET_DLL_HANDLE_EX_PIN) {
LdrDataTableEntry->LoadCount = 0xffff;
LdrpPinLoadedDll(LdrDataTableEntry);
}
else {
LdrDataTableEntry->LoadCount++;
LdrpReferenceLoadedDll(LdrDataTableEntry);
}
LdrpClearLoadInProgress();
}
}
if (DllHandle != NULL) {
*DllHandle = (PVOID)LdrDataTableEntry->DllBase;
}
}
} __finally {
if (DynamicRedirectedDllName.Buffer != NULL) {
RtlFreeUnicodeString(&DynamicRedirectedDllName);
}
if (ActualDllNameStr.Buffer != NULL) {
RtlFreeHeap(RtlProcessHeap(), 0, (PVOID)ActualDllNameStr.Buffer);
ActualDllNameStr.Buffer = NULL;
}
if (HoldingLoaderLock) {
LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
HoldingLoaderLock = FALSE;
}
}
return st;
}
NTSTATUS
LdrDisableThreadCalloutsForDll (
IN PVOID DllHandle
)
/*++
Routine Description:
This function disables thread attach and detach notification
for the specified DLL.
Arguments:
DllHandle - Supplies a handle to the DLL to disable.
Return Value:
NTSTATUS.
--*/
{
NTSTATUS st = STATUS_SUCCESS;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry = NULL;
const BOOLEAN InLdrInit = LdrpInLdrInit;
BOOL HoldingLoaderLock = FALSE;
PVOID LockCookie = NULL;
if ( LdrpShutdownInProgress ) {
return STATUS_SUCCESS;
}
try {
if ( InLdrInit == FALSE ) {
st = LdrLockLoaderLock(0, NULL, &LockCookie);
if (!NT_SUCCESS(st))
goto Exit;
HoldingLoaderLock = TRUE;
}
if (LdrpCheckForLoadedDllHandle(DllHandle, &LdrDataTableEntry)) {
if ( LdrDataTableEntry->TlsIndex ) {
st = STATUS_DLL_NOT_FOUND;
}
else {
LdrDataTableEntry->Flags |= LDRP_DONT_CALL_FOR_THREADS;
}
}
Exit:
;
}
finally {
if (HoldingLoaderLock) {
LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
HoldingLoaderLock = FALSE;
}
}
return st;
}
ULONG LdrpUnloadIndex = 0;
RTL_UNLOAD_EVENT_TRACE RtlpUnloadEventTrace[RTL_UNLOAD_EVENT_TRACE_NUMBER];
NTSYSAPI
PRTL_UNLOAD_EVENT_TRACE
NTAPI
RtlGetUnloadEventTrace (
VOID
)
{
return RtlpUnloadEventTrace;
}
VOID
LdrpRecordUnloadEvent (
IN PLDR_DATA_TABLE_ENTRY LdrDataTableEntry
)
/*++
Routine Description:
This function records in a ring buffer the last few dll unloads
Arguments:
LdrDataTableEntry - The ldr entry for this dll
Return Value:
None.
--*/
{
ULONG Seq, i, Len;
PVOID BaseAddress;
PIMAGE_NT_HEADERS NtHeaders;
Seq = LdrpUnloadIndex++;
i = Seq % RTL_UNLOAD_EVENT_TRACE_NUMBER;
BaseAddress = LdrDataTableEntry->DllBase;
RtlpUnloadEventTrace[i].Sequence = Seq;
RtlpUnloadEventTrace[i].BaseAddress = BaseAddress;
RtlpUnloadEventTrace[i].SizeOfImage = LdrDataTableEntry->SizeOfImage;
Len = LdrDataTableEntry->BaseDllName.Length;
if (Len > sizeof (RtlpUnloadEventTrace[i].ImageName)) {
Len = sizeof (RtlpUnloadEventTrace[i].ImageName);
}
RtlCopyMemory (RtlpUnloadEventTrace[i].ImageName,
LdrDataTableEntry->BaseDllName.Buffer,
Len);
if (Len < sizeof (RtlpUnloadEventTrace[i].ImageName)) {
RtlpUnloadEventTrace[i].ImageName[Len/sizeof (WCHAR)] = L'\0';
}
NtHeaders = RtlImageNtHeader (BaseAddress);
if (NtHeaders != NULL) {
RtlpUnloadEventTrace[i].TimeDateStamp = NtHeaders->FileHeader.TimeDateStamp;
RtlpUnloadEventTrace[i].CheckSum = NtHeaders->OptionalHeader.CheckSum;
} else {
RtlpUnloadEventTrace[i].TimeDateStamp = 0;
RtlpUnloadEventTrace[i].CheckSum = 0;
}
}
NTSTATUS
LdrUnloadDll (
IN PVOID DllHandle
)
/*++
Routine Description:
This function unloads the DLL from the specified process
Arguments:
DllHandle - Supplies a handle to the DLL to unload.
Return Value:
NTSTATUS.
--*/
{
NTSTATUS st;
PPEB Peb;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PLDR_DATA_TABLE_ENTRY Entry;
PDLL_INIT_ROUTINE InitRoutine;
LIST_ENTRY LocalUnloadHead;
PLIST_ENTRY Next;
ULONG Cor20HeaderSize;
PIMAGE_COR20_HEADER *Cor20Header;
PRTL_PATCH_HEADER RundownPatchList = NULL;
Peb = NtCurrentPeb();
st = STATUS_SUCCESS;
//
// Grab Peb lock and decrement reference count of all affected DLLs
//
if (!LdrpInLdrInit) {
RtlEnterCriticalSection(&LdrpLoaderLock);
}
try {
LdrpActiveUnloadCount += 1;
if (LdrpShutdownInProgress) {
goto leave_finally;
}
if (!LdrpCheckForLoadedDllHandle(DllHandle, &LdrDataTableEntry)) {
st = STATUS_DLL_NOT_FOUND;
goto leave_finally;
}
//
// Now that we have the data table entry, unload it
//
if (LdrDataTableEntry->LoadCount != 0xffff) {
LdrDataTableEntry->LoadCount -= 1;
if (LdrDataTableEntry->Flags & LDRP_IMAGE_DLL) {
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME Frame = { sizeof(Frame), RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER };
RtlActivateActivationContextUnsafeFast(&Frame, LdrDataTableEntry->EntryPointActivationContext);
__try {
LdrpDereferenceLoadedDll(LdrDataTableEntry);
} __finally {
RtlDeactivateActivationContextUnsafeFast(&Frame);
}
}
} else {
//
// if the load count is 0xffff, then we do not need to recurse
// through this DLL's import table.
//
// Additionally, we don't have to scan more LoadCount == 0
// modules since nothing could have happened as a result of a free on this
// DLL.
goto leave_finally;
}
//
// Now process init routines and then in a second pass, unload
// DLLs
//
if (ShowSnaps) {
DbgPrint("LDR: UNINIT LIST\n");
}
if (LdrpActiveUnloadCount == 1) {
InitializeListHead(&LdrpUnloadHead);
}
//
// Go in reverse order initialization order and build
// the unload list
//
Next = PebLdr.InInitializationOrderModuleList.Blink;
while ( Next != &PebLdr.InInitializationOrderModuleList) {
LdrDataTableEntry
= (PLDR_DATA_TABLE_ENTRY)
(CONTAINING_RECORD(Next,LDR_DATA_TABLE_ENTRY,InInitializationOrderLinks));
Next = Next->Blink;
LdrDataTableEntry->Flags &= ~LDRP_UNLOAD_IN_PROGRESS;
if (LdrDataTableEntry->LoadCount == 0) {
if (ShowSnaps) {
DbgPrint(" (%d) [%ws] %ws (%lx) deinit %lx\n",
LdrpActiveUnloadCount,
LdrDataTableEntry->BaseDllName.Buffer,
LdrDataTableEntry->FullDllName.Buffer,
(ULONG)LdrDataTableEntry->LoadCount,
LdrDataTableEntry->EntryPoint
);
}
Entry = LdrDataTableEntry;
//
// Shim engine callback. Remove it from the shim list of hooked modules
//
if (g_pfnSE_DllUnloaded != NULL) {
(*g_pfnSE_DllUnloaded)(Entry);
}
RemoveEntryList(&Entry->InInitializationOrderLinks);
RemoveEntryList(&Entry->InMemoryOrderLinks);
RemoveEntryList(&Entry->HashLinks);
if ( LdrpActiveUnloadCount > 1 ) {
LdrpLoadedDllHandleCache = NULL;
Entry->InMemoryOrderLinks.Flink = NULL;
}
InsertTailList(&LdrpUnloadHead,&Entry->HashLinks);
}
}
//
// End of new code
//
//
// We only do init routine call's and module free's at the top level,
// so if the active count is > 1, just return
//
if (LdrpActiveUnloadCount > 1 ) {
goto leave_finally;
}
//
// Now that the unload list is built, walk through the unload
// list in order and call the init routine. The dll must remain
// on the InLoadOrderLinks so that the pctoheader stuff will
// still work
//
InitializeListHead(&LocalUnloadHead);
Entry = NULL;
Next = LdrpUnloadHead.Flink;
while ( Next != &LdrpUnloadHead ) {
top:
if ( Entry ) {
#if defined(_AMD64_) || defined(_IA64_)
RtlRemoveInvertedFunctionTable(&LdrpInvertedFunctionTable,
Entry->DllBase);
#endif
RemoveEntryList(&(Entry->InLoadOrderLinks));
Entry = NULL;
Next = LdrpUnloadHead.Flink;
if (Next == &LdrpUnloadHead ) {
goto bottom;
}
}
LdrDataTableEntry
= (PLDR_DATA_TABLE_ENTRY)
(CONTAINING_RECORD(Next,LDR_DATA_TABLE_ENTRY,HashLinks));
LdrpRecordUnloadEvent (LdrDataTableEntry);
//
// Remove dll from the global unload list and place
// on the local unload list. This is because the global list
// can change during the callout to the init routine
//
Entry = LdrDataTableEntry;
LdrpLoadedDllHandleCache = NULL;
Entry->InMemoryOrderLinks.Flink = NULL;
RemoveEntryList(&Entry->HashLinks);
InsertTailList(&LocalUnloadHead,&Entry->HashLinks);
//
// If the function has an init routine, call it.
//
InitRoutine = (PDLL_INIT_ROUTINE)(ULONG_PTR)LdrDataTableEntry->EntryPoint;
if (InitRoutine && (LdrDataTableEntry->Flags & LDRP_PROCESS_ATTACH_CALLED) ) {
try {
if (ShowSnaps) {
DbgPrint("LDR: Calling deinit %lx\n",InitRoutine);
}
LDRP_ACTIVATE_ACTIVATION_CONTEXT(LdrDataTableEntry);
LdrpCallInitRoutine(InitRoutine,
LdrDataTableEntry->DllBase,
DLL_PROCESS_DETACH,
NULL);
LDRP_DEACTIVATE_ACTIVATION_CONTEXT();
#if defined(_AMD64_) || defined(_IA64_)
RtlRemoveInvertedFunctionTable(&LdrpInvertedFunctionTable,
Entry->DllBase);
#endif
RemoveEntryList(&Entry->InLoadOrderLinks);
Entry = NULL;
Next = LdrpUnloadHead.Flink;
}
except(LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)){
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
"LDR: %s - exception %08lx caught while sending DLL_PROCESS_DETACH\n",
__FUNCTION__,
GetExceptionCode());
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
" Dll Name: %wZ\n",
&LdrDataTableEntry->FullDllName);
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
" InitRoutine: %p\n",
InitRoutine);
goto top;
}
} else {
#if defined(_AMD64_) || defined(_IA64_)
RtlRemoveInvertedFunctionTable(&LdrpInvertedFunctionTable,
Entry->DllBase);
#endif
RemoveEntryList(&(Entry->InLoadOrderLinks));
Entry = NULL;
Next = LdrpUnloadHead.Flink;
}
}
bottom:
//
// Now, go through the modules and unmap them
//
Next = LocalUnloadHead.Flink;
while ( Next != &LocalUnloadHead ) {
LdrDataTableEntry
= (PLDR_DATA_TABLE_ENTRY)
(CONTAINING_RECORD(Next,LDR_DATA_TABLE_ENTRY,HashLinks));
Next = Next->Flink;
Entry = LdrDataTableEntry;
//
// Notify verifier that a dll will be unloaded.
//
// Now that we called the all the init routines with `detach'
// there is no excuse if we find a live CS in that region.
//
// Note: gdi32.dll's critical sections are deleted only on
// user32.dll'd DllMain( DLL_PROCESS_DETACH ) so we cannot
// do this check for leaked critical sections prior to this point.
//
if (Peb->NtGlobalFlag & FLG_APPLICATION_VERIFIER) {
AVrfDllUnloadNotification (LdrDataTableEntry);
}
//
// Unmap this DLL.
//
if (ShowSnaps) {
DbgPrint("LDR: Unmapping [%ws]\n",
LdrDataTableEntry->BaseDllName.Buffer
);
}
Cor20Header = RtlImageDirectoryEntryToData(Entry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR,
&Cor20HeaderSize);
if (Cor20Header != NULL) {
LdrpCorUnloadImage(Entry->DllBase);
}
if (!(Entry->Flags & LDRP_COR_OWNS_UNMAP)) {
st = NtUnmapViewOfSection(NtCurrentProcess(),Entry->DllBase);
ASSERT(NT_SUCCESS(st));
}
LdrUnloadAlternateResourceModule(Entry->DllBase);
LdrpSendDllNotifications (Entry,
LDR_DLL_NOTIFICATION_REASON_UNLOADED,
(LdrpShutdownInProgress ? LDR_DLL_UNLOADED_FLAG_PROCESS_TERMINATION : 0));
//
// See if we have hotpatch information and push each hotpatch block
// to the rundown list
//
while (Entry->PatchInformation) {
PRTL_PATCH_HEADER PatchHead = Entry->PatchInformation;
Entry->PatchInformation = PatchHead->NextPatch;
PatchHead->NextPatch = RundownPatchList;
RundownPatchList = PatchHead;
}
LdrpFinalizeAndDeallocateDataTableEntry(Entry);
if ( Entry == LdrpGetModuleHandleCache ) {
LdrpGetModuleHandleCache = NULL;
}
}
leave_finally:;
}
finally {
LdrpActiveUnloadCount -= 1;
if (!LdrpInLdrInit) {
RtlLeaveCriticalSection(&LdrpLoaderLock);
}
}
if ( RundownPatchList ) {
LdrpRundownHotpatchList( RundownPatchList );
}
return st;
}
NTSTATUS
LdrGetProcedureAddress (
IN PVOID DllHandle,
IN CONST ANSI_STRING* ProcedureName OPTIONAL,
IN ULONG ProcedureNumber OPTIONAL,
OUT PVOID *ProcedureAddress
)
{
return LdrpGetProcedureAddress(DllHandle,ProcedureName,ProcedureNumber,ProcedureAddress,TRUE);
}
NTSTATUS
LdrpGetProcedureAddress (
IN PVOID DllHandle,
IN CONST ANSI_STRING* ProcedureName OPTIONAL,
IN ULONG ProcedureNumber OPTIONAL,
OUT PVOID *ProcedureAddress,
IN BOOLEAN RunInitRoutines
)
/*++
Routine Description:
This function locates the address of the specified procedure in the
specified DLL and returns its address.
Arguments:
DllHandle - Supplies a handle to the DLL that the address is being
looked up in.
ProcedureName - Supplies that address of a string that contains the
name of the procedure to lookup in the DLL. If this argument is
not specified, then the ProcedureNumber is used.
ProcedureNumber - Supplies the procedure number to lookup. If
ProcedureName is specified, then this argument is ignored.
Otherwise, it specifies the procedure ordinal number to locate
in the DLL.
ProcedureAddress - Returns the address of the procedure found in
the DLL.
Return Value:
NTSTATUS.
--*/
{
NTSTATUS st;
UCHAR FunctionNameBuffer[64];
ULONG cb, ExportSize;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
IMAGE_THUNK_DATA Thunk;
PVOID ImageBase;
PIMAGE_IMPORT_BY_NAME FunctionName;
PCIMAGE_EXPORT_DIRECTORY ExportDirectory;
PLIST_ENTRY Next;
if (ShowSnaps) {
DbgPrint("LDR: LdrGetProcedureAddress by ");
}
RtlZeroMemory( &Thunk, sizeof( Thunk ) );
FunctionName = NULL;
if ( ARGUMENT_PRESENT(ProcedureName) ) {
if (ShowSnaps) {
DbgPrint("NAME - %s\n", ProcedureName->Buffer);
}
cb = ProcedureName->Length + FIELD_OFFSET(IMAGE_IMPORT_BY_NAME, Name) +
sizeof( UCHAR );
if (cb > MAXUSHORT) {
return STATUS_NAME_TOO_LONG;
}
if (cb > sizeof( FunctionNameBuffer )) {
FunctionName = (PIMAGE_IMPORT_BY_NAME)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( TEMP_TAG ),
cb
);
if ( !FunctionName ) {
return STATUS_INVALID_PARAMETER;
}
} else {
FunctionName = (PIMAGE_IMPORT_BY_NAME) FunctionNameBuffer;
}
FunctionName->Hint = 0;
cb = ProcedureName->Length;
RtlCopyMemory (FunctionName->Name, ProcedureName->Buffer, cb);
FunctionName->Name[cb] = '\0';
//
// Make sure we don't pass in address with high bit set so we
// can still use it as ordinal flag
//
ImageBase = FunctionName;
Thunk.u1.AddressOfData = 0;
} else {
ImageBase = NULL;
if (ShowSnaps) {
DbgPrint("ORDINAL - %lx\n", ProcedureNumber);
}
if (ProcedureNumber) {
Thunk.u1.Ordinal = ProcedureNumber | IMAGE_ORDINAL_FLAG;
} else {
return STATUS_INVALID_PARAMETER;
}
}
st = STATUS_ACCESS_VIOLATION;
if (!LdrpInLdrInit) {
RtlEnterCriticalSection (&LdrpLoaderLock);
}
try {
if (!LdrpCheckForLoadedDllHandle (DllHandle, &LdrDataTableEntry)) {
st = STATUS_DLL_NOT_FOUND;
leave;
}
ExportDirectory = (PCIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(
LdrDataTableEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize);
if (!ExportDirectory) {
st = STATUS_PROCEDURE_NOT_FOUND;
leave;
}
st = LdrpSnapThunk(LdrDataTableEntry->DllBase,
ImageBase,
&Thunk,
&Thunk,
ExportDirectory,
ExportSize,
FALSE,
NULL);
if (NT_SUCCESS(st) && RunInitRoutines) {
PLDR_DATA_TABLE_ENTRY LdrInitEntry;
//
// Look at last entry in init order list. If entry processed
// flag is not set, then a forwarded dll was loaded during the
// getprocaddr call and we need to run init routines
//
Next = PebLdr.InInitializationOrderModuleList.Blink;
LdrInitEntry = CONTAINING_RECORD(Next,
LDR_DATA_TABLE_ENTRY,
InInitializationOrderLinks);
if ( !(LdrInitEntry->Flags & LDRP_ENTRY_PROCESSED) ) {
//
// Shim engine callback. This is the chance to patch
// dynamically loaded modules.
//
try {
st = LdrpRunInitializeRoutines(NULL);
}
except(LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
st = GetExceptionCode();
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
"LDR: %s - Exception %x thrown by LdrpRunInitializeRoutines\n",
__FUNCTION__,
st);
}
}
}
if ( NT_SUCCESS(st) ) {
*ProcedureAddress = (PVOID)Thunk.u1.Function;
}
} finally {
if ( FunctionName && (FunctionName != (PIMAGE_IMPORT_BY_NAME) FunctionNameBuffer) ) {
RtlFreeHeap(RtlProcessHeap(),0,FunctionName);
}
if (!LdrpInLdrInit) {
RtlLeaveCriticalSection(&LdrpLoaderLock);
}
}
return st;
}
NTSTATUS
NTAPI
LdrVerifyImageMatchesChecksum (
IN HANDLE ImageFileHandle,
IN PLDR_IMPORT_MODULE_CALLBACK ImportCallbackRoutine OPTIONAL,
IN PVOID ImportCallbackParameter,
OUT PUSHORT ImageCharacteristics OPTIONAL
)
{
NTSTATUS Status;
HANDLE Section;
PVOID ViewBase;
SIZE_T ViewSize;
IO_STATUS_BLOCK IoStatusBlock;
FILE_STANDARD_INFORMATION StandardInfo;
PIMAGE_SECTION_HEADER LastRvaSection;
BOOLEAN b = FALSE;
BOOLEAN JustDoSideEffects;
//
// stevewo added all sorts of side effects to this API. We want to stop
// doing checksums for known dll's, but really want the sideeffects
// (ImageCharacteristics write, and Import descriptor walk).
//
if ( (UINT_PTR) ImageFileHandle & 1 ) {
JustDoSideEffects = TRUE;
}
else {
JustDoSideEffects = FALSE;
}
Status = NtCreateSection (&Section,
SECTION_MAP_EXECUTE,
NULL,
NULL,
PAGE_EXECUTE,
SEC_COMMIT,
ImageFileHandle);
if (!NT_SUCCESS(Status)) {
return Status;
}
ViewBase = NULL;
ViewSize = 0;
Status = NtMapViewOfSection (Section,
NtCurrentProcess(),
(PVOID *)&ViewBase,
0L,
0L,
NULL,
&ViewSize,
ViewShare,
0L,
PAGE_EXECUTE);
if ( !NT_SUCCESS(Status) ) {
NtClose(Section);
return Status;
}
//
// now the image is mapped as a data file... Calculate it's size and then
// check it's checksum
//
Status = NtQueryInformationFile(
ImageFileHandle,
&IoStatusBlock,
&StandardInfo,
sizeof(StandardInfo),
FileStandardInformation
);
if ( !NT_SUCCESS(Status) ) {
NtUnmapViewOfSection(NtCurrentProcess(),ViewBase);
NtClose(Section);
return Status;
}
try {
if ( JustDoSideEffects ) {
b = TRUE;
}
else {
b = LdrVerifyMappedImageMatchesChecksum(ViewBase,StandardInfo.EndOfFile.LowPart);
}
if (b && ARGUMENT_PRESENT( (ULONG_PTR)ImportCallbackRoutine )) {
PIMAGE_NT_HEADERS NtHeaders;
PCIMAGE_IMPORT_DESCRIPTOR ImportDescriptor;
ULONG ImportSize;
PCHAR ImportName;
//
// Caller wants to enumerate the import descriptors while we have
// the image mapped. Call back to their routine for each module
// name in the import descriptor table.
//
LastRvaSection = NULL;
NtHeaders = RtlImageNtHeader( ViewBase );
if (! NtHeaders) {
b = FALSE;
leave;
}
if (ARGUMENT_PRESENT( ImageCharacteristics )) {
*ImageCharacteristics = NtHeaders->FileHeader.Characteristics;
}
ImportDescriptor = (PCIMAGE_IMPORT_DESCRIPTOR)
RtlImageDirectoryEntryToData( ViewBase,
FALSE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&ImportSize
);
if (ImportDescriptor != NULL) {
while (ImportDescriptor->Name) {
ImportName = (PSZ)RtlImageRvaToVa( NtHeaders,
ViewBase,
ImportDescriptor->Name,
&LastRvaSection
);
(*ImportCallbackRoutine)( ImportCallbackParameter, ImportName );
ImportDescriptor += 1;
}
}
}
}
except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
"LDR: %s - caught exception %08lx while checking image checksums\n",
__FUNCTION__,
GetExceptionCode());
NtUnmapViewOfSection(NtCurrentProcess(),ViewBase);
NtClose(Section);
return STATUS_IMAGE_CHECKSUM_MISMATCH;
}
NtUnmapViewOfSection(NtCurrentProcess(),ViewBase);
NtClose(Section);
if ( !b ) {
Status = STATUS_IMAGE_CHECKSUM_MISMATCH;
}
return Status;
}
NTSTATUS
LdrReadMemory(
IN HANDLE Process OPTIONAL,
IN PVOID BaseAddress,
IN OUT PVOID Buffer,
IN SIZE_T Size)
{
NTSTATUS Status = STATUS_SUCCESS;
if (ARGUMENT_PRESENT( Process )) {
SIZE_T nRead;
Status = NtReadVirtualMemory(Process, BaseAddress, Buffer, Size, &nRead);
if (NT_SUCCESS( Status ) && (Size != nRead)) {
Status = STATUS_UNSUCCESSFUL;
}
}
else {
__try {
RtlCopyMemory(Buffer, BaseAddress, Size);
}
__except(LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
"LDR: %s - exception %08lx caught while copying %u bytes from %p to %p\n",
__FUNCTION__,
GetExceptionCode(),
BaseAddress,
Buffer);
if (NT_SUCCESS(Status = GetExceptionCode())) {
Status = STATUS_UNSUCCESSFUL;
}
}
}
return Status;
}
NTSTATUS
LdrGetModuleName(
IN HANDLE Process OPTIONAL,
IN PCUNICODE_STRING LdrFullDllName,
IN OUT PRTL_PROCESS_MODULE_INFORMATION ModuleInfo,
IN BOOL Wow64Redirect)
{
NTSTATUS Status;
UNICODE_STRING FullDllName;
ANSI_STRING AnsiString;
PCHAR s;
WCHAR Buffer[ LDR_NUMBER_OF(ModuleInfo->FullPathName) + 1];
USHORT Length = (USHORT)min(LdrFullDllName->Length,
sizeof(Buffer) - sizeof(Buffer[0]));
Status = LdrReadMemory(Process,
LdrFullDllName->Buffer,
Buffer,
Length);
if (!NT_SUCCESS( Status )) {
return Status;
}
Buffer[LDR_NUMBER_OF(Buffer) - 1] = UNICODE_NULL; // Ensure NULL termination
#if defined(_WIN64)
if (Wow64Redirect) {
C_ASSERT( WOW64_SYSTEM_DIRECTORY_U_SIZE ==
(sizeof(L"system32") - sizeof(WCHAR)));
// including preceding '\\' if exists
SIZE_T System32Offset = wcslen(USER_SHARED_DATA->NtSystemRoot);
ASSERT(System32Offset != 0);
if (USER_SHARED_DATA->NtSystemRoot[System32Offset - 1] == L'\\') {
--System32Offset;
}
if (!_wcsnicmp(Buffer, USER_SHARED_DATA->NtSystemRoot, System32Offset) &&
!_wcsnicmp(Buffer + System32Offset,
L"\\system32",
WOW64_SYSTEM_DIRECTORY_U_SIZE / sizeof(WCHAR) + 1)) {
RtlCopyMemory(Buffer + System32Offset + 1,
WOW64_SYSTEM_DIRECTORY_U,
WOW64_SYSTEM_DIRECTORY_U_SIZE);
}
}
#else
UNREFERENCED_PARAMETER (Wow64Redirect);
#endif // defined(_WIN64)
FullDllName.Buffer = Buffer;
FullDllName.Length = FullDllName.MaximumLength = Length;
AnsiString.Buffer = (PCHAR)ModuleInfo->FullPathName;
AnsiString.Length = 0;
AnsiString.MaximumLength = sizeof( ModuleInfo->FullPathName );
Status = RtlUnicodeStringToAnsiString(&AnsiString,
&FullDllName,
FALSE);
if (!NT_SUCCESS (Status)) {
return Status;
}
s = AnsiString.Buffer + AnsiString.Length;
while (s > AnsiString.Buffer && *--s) {
if (*s == (UCHAR)OBJ_NAME_PATH_SEPARATOR) {
s++;
break;
}
}
ModuleInfo->OffsetToFileName = (USHORT)(s - AnsiString.Buffer);
return STATUS_SUCCESS;
}
NTSTATUS
LdrQueryProcessPeb (
IN HANDLE Process OPTIONAL,
IN OUT PPEB* Peb)
{
NTSTATUS Status;
PROCESS_BASIC_INFORMATION BasicInfo;
if (ARGUMENT_PRESENT (Process)) {
Status = NtQueryInformationProcess (Process,
ProcessBasicInformation,
&BasicInfo,
sizeof(BasicInfo),
NULL);
if (NT_SUCCESS (Status)) {
*Peb = BasicInfo.PebBaseAddress;
}
}
else {
*Peb = NtCurrentPeb ();
Status = STATUS_SUCCESS;
}
return Status;
}
NTSTATUS
LdrQueryInLoadOrderModuleList (
IN HANDLE Process OPTIONAL,
IN OUT PLIST_ENTRY* Head,
IN OUT PLIST_ENTRY* InInitOrderHead OPTIONAL
)
{
PPEB_LDR_DATA Ldr;
UNREFERENCED_PARAMETER (Process);
Ldr = &PebLdr;
*Head = &Ldr->InLoadOrderModuleList;
if (ARGUMENT_PRESENT (InInitOrderHead)) {
*InInitOrderHead = &Ldr->InInitializationOrderModuleList;
}
return STATUS_SUCCESS;
}
NTSTATUS
LdrQueryNextListEntry (
IN HANDLE Process OPTIONAL,
IN PLIST_ENTRY Head,
IN OUT PLIST_ENTRY* Tail
)
{
return LdrReadMemory (Process, &Head->Flink, Tail, sizeof(*Tail));
}
NTSTATUS
LdrQueryModuleInfoFromLdrEntry (
IN HANDLE Process OPTIONAL,
IN PRTL_PROCESS_MODULES ModuleInformation,
IN OUT PRTL_PROCESS_MODULE_INFORMATION ModuleInfo,
IN PLIST_ENTRY LdrEntry,
IN PLIST_ENTRY InitOrderList)
{
NTSTATUS Status;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntryPtr;
LDR_DATA_TABLE_ENTRY LdrDataTableEntry;
UNREFERENCED_PARAMETER (ModuleInformation);
LdrDataTableEntryPtr = CONTAINING_RECORD(LdrEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
Status = LdrReadMemory(Process,
LdrEntry,
&LdrDataTableEntry,
sizeof(LdrDataTableEntry));
if (!NT_SUCCESS (Status)) {
return Status;
}
ModuleInfo->ImageBase = LdrDataTableEntry.DllBase;
ModuleInfo->ImageSize = LdrDataTableEntry.SizeOfImage;
ModuleInfo->Flags = LdrDataTableEntry.Flags;
ModuleInfo->LoadCount = LdrDataTableEntry.LoadCount;
if (!ARGUMENT_PRESENT( Process )) {
UINT LoopDetectorCount = 10240; // 10K modules max
PLIST_ENTRY Next1 = InitOrderList->Flink;
while ( Next1 != InitOrderList ) {
PLDR_DATA_TABLE_ENTRY Entry1 =
CONTAINING_RECORD(Next1,
LDR_DATA_TABLE_ENTRY,
InInitializationOrderLinks);
ModuleInfo->InitOrderIndex++;
if ((LdrDataTableEntryPtr == Entry1) ||
(!LoopDetectorCount--))
{
break;
}
Next1 = Next1->Flink;
}
}
Status = LdrGetModuleName(Process,
&LdrDataTableEntry.FullDllName,
ModuleInfo,
FALSE);
return Status;
}
PRTL_CRITICAL_SECTION
LdrQueryModuleInfoLocalLoaderLock (
VOID
)
{
PRTL_CRITICAL_SECTION LoaderLock = NULL;
if (!LdrpInLdrInit) {
LoaderLock = &LdrpLoaderLock;
if (LoaderLock != NULL) {
RtlEnterCriticalSection (LoaderLock);
}
}
return LoaderLock;
}
VOID
LdrQueryModuleInfoLocalLoaderUnlock (
IN PRTL_CRITICAL_SECTION LoaderLock
)
{
if (LoaderLock) {
RtlLeaveCriticalSection(LoaderLock);
}
}
#if defined(_WIN64)
NTSTATUS
LdrQueryProcessPeb32(
IN HANDLE Process OPTIONAL,
IN OUT PPEB32* Peb
)
{
NTSTATUS Status;
HANDLE TargetProcess;
if (ARGUMENT_PRESENT (Process)) {
TargetProcess = Process;
}
else {
TargetProcess = NtCurrentProcess ();
}
Status = NtQueryInformationProcess (TargetProcess,
ProcessWow64Information,
Peb,
sizeof(*Peb),
NULL);
return Status;
}
NTSTATUS
LdrQueryInLoadOrderModuleList32(
IN HANDLE Process OPTIONAL,
IN OUT PLIST_ENTRY32 *Head,
IN OUT PLIST_ENTRY32 *InInitOrderHead OPTIONAL
)
{
NTSTATUS Status;
PPEB32 Peb;
PPEB_LDR_DATA32 Ldr;
ULONG32 Ptr32;
Status = LdrQueryProcessPeb32 (Process, &Peb);
if (!NT_SUCCESS (Status)) {
return Status;
}
if (!Peb) {
//
// The process isn't a WOW process.
//
*Head = NULL;
return STATUS_SUCCESS;
}
//
// Ldr = Peb->Ldr
//
Status = LdrReadMemory (Process, &Peb->Ldr, &Ptr32, sizeof(Ptr32));
if (!NT_SUCCESS (Status)) {
return Status;
}
Ldr = (PPEB_LDR_DATA32)(ULONG_PTR) Ptr32;
if (!Ldr) {
*Head = NULL;
return STATUS_SUCCESS;
}
*Head = &Ldr->InLoadOrderModuleList;
if (ARGUMENT_PRESENT (InInitOrderHead)) {
*InInitOrderHead = &Ldr->InInitializationOrderModuleList;
}
return Status;
}
NTSTATUS
LdrQueryNextListEntry32 (
IN HANDLE Process OPTIONAL,
IN PLIST_ENTRY32 Head,
IN OUT PLIST_ENTRY32 *Tail
)
{
NTSTATUS Status;
ULONG32 Ptr32;
Status = LdrReadMemory (Process, &Head->Flink, &Ptr32, sizeof(Ptr32));
*Tail = (PLIST_ENTRY32)(ULONG_PTR)Ptr32;
return Status;
}
NTSTATUS
LdrQueryModuleInfoFromLdrEntry32 (
IN HANDLE Process OPTIONAL,
IN PRTL_PROCESS_MODULES ModuleInformation,
IN OUT PRTL_PROCESS_MODULE_INFORMATION ModuleInfo,
IN PLIST_ENTRY32 LdrEntry,
IN PLIST_ENTRY32 InitOrderList
)
{
NTSTATUS Status;
PLDR_DATA_TABLE_ENTRY32 LdrDataTableEntryPtr;
LDR_DATA_TABLE_ENTRY32 LdrDataTableEntry;
UNICODE_STRING FullDllName;
UNREFERENCED_PARAMETER (ModuleInformation);
LdrDataTableEntryPtr = CONTAINING_RECORD(LdrEntry,
LDR_DATA_TABLE_ENTRY32,
InLoadOrderLinks);
Status = LdrReadMemory (Process,
LdrEntry,
&LdrDataTableEntry,
sizeof(LdrDataTableEntry));
if (!NT_SUCCESS (Status)) {
return Status;
}
ModuleInfo->ImageBase = (PVOID)(ULONG_PTR) LdrDataTableEntry.DllBase;
ModuleInfo->ImageSize = LdrDataTableEntry.SizeOfImage;
ModuleInfo->Flags = LdrDataTableEntry.Flags;
ModuleInfo->LoadCount = LdrDataTableEntry.LoadCount;
if (!ARGUMENT_PRESENT( Process )) {
UINT LoopDetectorCount = 500;
PLIST_ENTRY32 Next1 = (PLIST_ENTRY32)(ULONG_PTR)
(InitOrderList->Flink);
while (Next1 != InitOrderList) {
PLDR_DATA_TABLE_ENTRY32 Entry1 =
CONTAINING_RECORD(Next1,
LDR_DATA_TABLE_ENTRY32,
InInitializationOrderLinks);
ModuleInfo->InitOrderIndex++;
if ((LdrDataTableEntryPtr == Entry1) ||
(!LoopDetectorCount--))
{
break;
}
Next1 = (PLIST_ENTRY32)(ULONG_PTR)(Next1->Flink);
}
}
FullDllName.Buffer = (PWSTR)(ULONG_PTR)LdrDataTableEntry.FullDllName.Buffer;
FullDllName.Length = LdrDataTableEntry.FullDllName.Length;
FullDllName.MaximumLength = LdrDataTableEntry.FullDllName.MaximumLength;
Status = LdrGetModuleName(Process, &FullDllName, ModuleInfo, TRUE);
return Status;
}
PRTL_CRITICAL_SECTION32
LdrQueryModuleInfoLocalLoaderLock32 (
VOID
)
{
return NULL;
}
VOID
LdrQueryModuleInfoLocalLoaderUnlock32 (
IN PRTL_CRITICAL_SECTION32 LoaderLock)
{
UNREFERENCED_PARAMETER (LoaderLock);
}
#endif // defined(_WIN64)
typedef
NTSTATUS
(*PLDR_QUERY_IN_LOAD_ORDER_MODULE_LIST)(
IN HANDLE Process OPTIONAL,
IN OUT PLIST_ENTRY* Head,
IN OUT PLIST_ENTRY* InInitOrderHead OPTIONAL);
typedef NTSTATUS
(*PLDR_QUERY_NEXT_LIST_ENTRY)(
IN HANDLE Process OPTIONAL,
IN PLIST_ENTRY Head,
IN OUT PLIST_ENTRY* Tail);
typedef
NTSTATUS
(*PLDR_QUERY_MODULE_INFO_FROM_LDR_ENTRY)(
IN HANDLE Process OPTIONAL,
IN PRTL_PROCESS_MODULES ModuleInformation,
IN OUT PRTL_PROCESS_MODULE_INFORMATION ModuleInfo,
IN PLIST_ENTRY LdrEntry,
IN PLIST_ENTRY InitOrderList);
typedef
PRTL_CRITICAL_SECTION
(*PLDR_QUERY_MODULE_INFO_LOCAL_LOADER_LOCK)(VOID);
typedef
VOID
(*PLDR_QUERY_MODULE_INFO_LOCAL_LOADER_UNLOCK)(PRTL_CRITICAL_SECTION);
static struct {
PLDR_QUERY_IN_LOAD_ORDER_MODULE_LIST LdrQueryInLoadOrderModuleList;
PLDR_QUERY_NEXT_LIST_ENTRY LdrQueryNextListEntry;
PLDR_QUERY_MODULE_INFO_FROM_LDR_ENTRY LdrQueryModuleInfoFromLdrEntry;
PLDR_QUERY_MODULE_INFO_LOCAL_LOADER_LOCK LdrQueryModuleInfoLocalLoaderLock;
PLDR_QUERY_MODULE_INFO_LOCAL_LOADER_UNLOCK LdrQueryModuleInfoLocalLoaderUnlock;
} LdrQueryMethods[] = {
{
LdrQueryInLoadOrderModuleList,
LdrQueryNextListEntry,
LdrQueryModuleInfoFromLdrEntry,
LdrQueryModuleInfoLocalLoaderLock,
LdrQueryModuleInfoLocalLoaderUnlock
}
#if defined(_WIN64)
,
{
(PLDR_QUERY_IN_LOAD_ORDER_MODULE_LIST)LdrQueryInLoadOrderModuleList32,
(PLDR_QUERY_NEXT_LIST_ENTRY)LdrQueryNextListEntry32,
(PLDR_QUERY_MODULE_INFO_FROM_LDR_ENTRY)LdrQueryModuleInfoFromLdrEntry32,
(PLDR_QUERY_MODULE_INFO_LOCAL_LOADER_LOCK)LdrQueryModuleInfoLocalLoaderLock32,
(PLDR_QUERY_MODULE_INFO_LOCAL_LOADER_UNLOCK)LdrQueryModuleInfoLocalLoaderUnlock32
}
#endif defined(_WIN64)
};
NTSTATUS
LdrQueryProcessModuleInformationEx(
IN HANDLE Process OPTIONAL,
IN ULONG_PTR Flags OPTIONAL,
OUT PRTL_PROCESS_MODULES ModuleInformation,
IN ULONG ModuleInformationLength,
OUT PULONG ReturnLength OPTIONAL)
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
PRTL_CRITICAL_SECTION LoaderLock = NULL;
SIZE_T mid;
ULONG RequiredLength = FIELD_OFFSET( RTL_PROCESS_MODULES, Modules );
PLIST_ENTRY List;
PLIST_ENTRY InInitOrderList;
PRTL_PROCESS_MODULE_INFORMATION ModuleInfo;
if (ModuleInformationLength < RequiredLength) {
Status = STATUS_INFO_LENGTH_MISMATCH;
ModuleInfo = NULL;
}
else {
ModuleInformation->NumberOfModules = 0;
ModuleInfo = &ModuleInformation->Modules[ 0 ];
Status = STATUS_SUCCESS;
}
for (mid = 0;
mid < (ARGUMENT_PRESENT( Flags ) ? LDR_NUMBER_OF(LdrQueryMethods) : 1);
++mid)
{
NTSTATUS Status1;
PLIST_ENTRY Entry;
__try {
UINT LoopDetectorCount = 10240; // allow not more than 10K modules
if ( !ARGUMENT_PRESENT( Process )) {
LoaderLock = LdrQueryMethods[mid].LdrQueryModuleInfoLocalLoaderLock();
}
Status1 = LdrQueryMethods[mid].LdrQueryInLoadOrderModuleList(Process, &List, &InInitOrderList);
if (!NT_SUCCESS( Status1 )) {
Status = Status1;
__leave;
}
if (!List) {
__leave;
}
Status1 = LdrQueryMethods[mid].LdrQueryNextListEntry(Process,
List,
&Entry);
if (!NT_SUCCESS( Status1 )) {
Status = Status1;
__leave;
}
while (Entry != List) {
if (!LoopDetectorCount--) {
Status = STATUS_FAIL_CHECK;
__leave;
}
RequiredLength += sizeof( RTL_PROCESS_MODULE_INFORMATION );
if (ModuleInformationLength < RequiredLength) {
Status = STATUS_INFO_LENGTH_MISMATCH;
}
else {
Status1 = LdrQueryMethods[mid].LdrQueryModuleInfoFromLdrEntry(Process,
ModuleInformation,
ModuleInfo,
Entry, InInitOrderList);
if (!NT_SUCCESS( Status1 )) {
Status = Status1;
__leave;
}
ModuleInfo++;
}
//
// NOTICE-2002/03/15-ELi
// This chould be non-NULL and not a valid access
// should check ModuleInfo or ModuleInformationLength instead
// Assuming ModuleInfo is not NULL when the code can safely
// reference ModuleInformation->NumberOfModules
//
if ((ModuleInfo != NULL) && (ModuleInformation != NULL)) {
ModuleInformation->NumberOfModules++;
}
Status1 = LdrQueryMethods[mid].LdrQueryNextListEntry(Process,
Entry,
&Entry);
if (!NT_SUCCESS( Status1 )) {
Status = Status1;
__leave;
}
} // while
}
__finally {
if (LoaderLock) {
LdrQueryMethods[mid].LdrQueryModuleInfoLocalLoaderUnlock(LoaderLock);
}
if (ARGUMENT_PRESENT( ReturnLength )) {
*ReturnLength = RequiredLength;
}
}
} // for
return Status;
}
NTSTATUS
LdrQueryProcessModuleInformation(
OUT PRTL_PROCESS_MODULES ModuleInformation,
IN ULONG ModuleInformationLength,
OUT PULONG ReturnLength OPTIONAL)
{
return LdrQueryProcessModuleInformationEx(NULL,
0,
ModuleInformation,
ModuleInformationLength,
ReturnLength);
}
NTSTATUS
NTAPI
LdrRegisterDllNotification (
ULONG Flags,
PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
PVOID Context,
PVOID *Cookie
)
{
NTSTATUS Status = STATUS_SUCCESS;
PLDRP_DLL_NOTIFICATION_BLOCK NotificationBlock = NULL;
BOOLEAN HoldingLoaderLock = FALSE;
const BOOLEAN InLdrInit = LdrpInLdrInit;
PVOID LockCookie = NULL;
__try {
if (Cookie != NULL) {
*Cookie = NULL;
}
if ((Flags != 0) ||
(Cookie == NULL) ||
(NotificationFunction == NULL)) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
NotificationBlock = (PLDRP_DLL_NOTIFICATION_BLOCK)
RtlAllocateHeap(RtlProcessHeap(), 0, sizeof(LDRP_DLL_NOTIFICATION_BLOCK));
if (NotificationBlock == NULL) {
Status = STATUS_NO_MEMORY;
goto Exit;
}
NotificationBlock->NotificationFunction = NotificationFunction;
NotificationBlock->Context = Context;
if (!InLdrInit) {
__try {
Status = LdrLockLoaderLock(0, NULL, &LockCookie);
if (!NT_SUCCESS(Status)) {
goto Exit;
}
HoldingLoaderLock = TRUE;
} __except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
Status = GetExceptionCode();
goto Exit;
}
}
InsertTailList(&LdrpDllNotificationList, &NotificationBlock->Links);
*Cookie = (PVOID) NotificationBlock;
NotificationBlock = NULL;
Status = STATUS_SUCCESS;
Exit:
;
} __finally {
if (HoldingLoaderLock) {
LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
HoldingLoaderLock = FALSE;
}
if (NotificationBlock != NULL) {
RtlFreeHeap(RtlProcessHeap(), 0, NotificationBlock);
}
}
return Status;
}
NTSTATUS
NTAPI
LdrUnregisterDllNotification (
PVOID Cookie
)
{
PLDRP_DLL_NOTIFICATION_BLOCK NotificationBlock;
NTSTATUS Status;
BOOLEAN HoldingLoaderLock;
BOOLEAN InLdrInit;
PVOID LockCookie;
if (Cookie == NULL) {
return STATUS_INVALID_PARAMETER;
}
Status = STATUS_SUCCESS;
HoldingLoaderLock = FALSE;
InLdrInit = LdrpInLdrInit;
LockCookie = NULL;
__try {
if (!InLdrInit) {
__try {
Status = LdrLockLoaderLock (0, NULL, &LockCookie);
if (!NT_SUCCESS(Status)) {
goto Exit;
}
HoldingLoaderLock = TRUE;
} __except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
Status = GetExceptionCode();
goto Exit;
}
}
NotificationBlock = CONTAINING_RECORD(LdrpDllNotificationList.Flink, LDRP_DLL_NOTIFICATION_BLOCK, Links);
while (&NotificationBlock->Links != &LdrpDllNotificationList) {
if (NotificationBlock == Cookie)
break;
NotificationBlock = CONTAINING_RECORD(NotificationBlock->Links.Flink, LDRP_DLL_NOTIFICATION_BLOCK, Links);
}
if (&NotificationBlock->Links != &LdrpDllNotificationList) {
RemoveEntryList(&NotificationBlock->Links);
RtlFreeHeap(RtlProcessHeap(), 0, NotificationBlock);
Status = STATUS_SUCCESS;
} else {
Status = STATUS_NOT_FOUND;
}
Exit:
;
} __finally {
if (HoldingLoaderLock) {
LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
HoldingLoaderLock = FALSE;
}
}
return Status;
}
VOID
LdrpSendDllNotifications (
IN PLDR_DATA_TABLE_ENTRY Entry,
IN ULONG NotificationType,
IN ULONG Flags
)
{
PLIST_ENTRY Next;
LDR_DLL_NOTIFICATION_DATA Data;
Data.Loaded.Flags = Flags;
Data.Loaded.FullDllName = &Entry->FullDllName;
Data.Loaded.BaseDllName = &Entry->BaseDllName;
Data.Loaded.DllBase = Entry->DllBase;
Data.Loaded.SizeOfImage = Entry->SizeOfImage;
Next = LdrpDllNotificationList.Flink;
while (Next != &LdrpDllNotificationList) {
PLDRP_DLL_NOTIFICATION_BLOCK Block = CONTAINING_RECORD(Next, LDRP_DLL_NOTIFICATION_BLOCK, Links);
__try {
(*Block->NotificationFunction)(NotificationType, &Data, Block->Context);
} __except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
// just go on to the next one...
}
Next = Next->Flink;
}
}
BOOLEAN
NTAPI
RtlDllShutdownInProgress (
VOID
)
/*++
Routine Description:
This routine returns the status of DLL shutdown.
Arguments:
None
Return Value:
BOOLEAN - TRUE: Shutdown is in progress, FALSE: Shutdown is not currently in progress.
--*/
{
if (LdrpShutdownInProgress) {
return TRUE;
} else {
return FALSE;
}
}
NTSTATUS
NTAPI
LdrLockLoaderLock (
ULONG Flags,
PULONG Disposition,
PVOID *Cookie
)
{
NTSTATUS Status;
BOOLEAN InLdrInit;
InLdrInit = LdrpInLdrInit;
if (Disposition != NULL) {
*Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_INVALID;
}
if (Cookie != NULL) {
*Cookie = NULL;
}
if ((Flags & ~(LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY | LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)) != 0) {
if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS) {
RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
}
Status = STATUS_INVALID_PARAMETER_1;
goto Exit;
}
if (Cookie == NULL) {
if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS) {
RtlRaiseStatus (STATUS_INVALID_PARAMETER_3);
}
Status = STATUS_INVALID_PARAMETER_3;
goto Exit;
}
//
// If you hit this assertion failure, you specified that you only wanted to
// try acquiring the lock, but you forgot to specify a Disposition out where
// this function could indicate whether the lock was actually acquired.
//
ASSERT((Disposition != NULL) || !(Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY));
if ((Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY) &&
(Disposition == NULL)) {
if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS) {
RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
}
Status = STATUS_INVALID_PARAMETER_2;
goto Exit;
}
if (InLdrInit) {
Status = STATUS_SUCCESS;
goto Exit;
}
if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS) {
if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY) {
if (RtlTryEnterCriticalSection(&LdrpLoaderLock)) {
*Cookie = (PVOID) MAKE_LOADER_LOCK_COOKIE(LOADER_LOCK_COOKIE_TYPE_NORMAL, InterlockedIncrement(&LdrpLoaderLockAcquisitionCount));
*Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
} else {
*Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
}
} else {
RtlEnterCriticalSection(&LdrpLoaderLock);
if (Disposition != NULL) {
*Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
}
*Cookie = (PVOID) MAKE_LOADER_LOCK_COOKIE(LOADER_LOCK_COOKIE_TYPE_NORMAL, InterlockedIncrement(&LdrpLoaderLockAcquisitionCount));
}
} else {
__try {
if (Flags & LDR_LOCK_LOADER_LOCK_FLAG_TRY_ONLY) {
if (RtlTryEnterCriticalSection(&LdrpLoaderLock)) {
*Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
*Cookie = (PVOID) MAKE_LOADER_LOCK_COOKIE(LOADER_LOCK_COOKIE_TYPE_NORMAL, InterlockedIncrement(&LdrpLoaderLockAcquisitionCount));
} else {
*Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_NOT_ACQUIRED;
}
} else {
RtlEnterCriticalSection(&LdrpLoaderLock);
if (Disposition != NULL) {
*Disposition = LDR_LOCK_LOADER_LOCK_DISPOSITION_LOCK_ACQUIRED;
}
*Cookie = (PVOID) MAKE_LOADER_LOCK_COOKIE(LOADER_LOCK_COOKIE_TYPE_NORMAL, InterlockedIncrement(&LdrpLoaderLockAcquisitionCount));
}
} __except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
Status = GetExceptionCode();
DbgPrintEx(
DPFLTR_LDR_ID,
LDR_ERROR_DPFLTR,
"LDR: %s - Caught exception %08lx\n",
__FUNCTION__,
Status);
goto Exit;
}
}
Status = STATUS_SUCCESS;
Exit:
return Status;
}
NTSTATUS
NTAPI
LdrUnlockLoaderLock(
ULONG Flags,
PVOID CookieIn
)
{
NTSTATUS Status;
const ULONG_PTR Cookie = (ULONG_PTR) CookieIn;
if ((Flags & ~(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)) != 0) {
if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
RtlRaiseStatus(STATUS_INVALID_PARAMETER_1);
Status = STATUS_INVALID_PARAMETER_1;
goto Exit;
}
if (CookieIn == NULL) {
Status = STATUS_SUCCESS;
goto Exit;
}
// A little validation on the cookie...
if (EXTRACT_LOADER_LOCK_COOKIE_TYPE(Cookie) != LOADER_LOCK_COOKIE_TYPE_NORMAL) {
if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
Status = STATUS_INVALID_PARAMETER_2;
goto Exit;
}
if (EXTRACT_LOADER_LOCK_COOKIE_TID(Cookie) != (HandleToUlong(NtCurrentTeb()->ClientId.UniqueThread) & LOADER_LOCK_COOKIE_TID_BIT_MASK)) {
if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS)
RtlRaiseStatus(STATUS_INVALID_PARAMETER_2);
Status = STATUS_INVALID_PARAMETER_2;
goto Exit;
}
if (Flags & LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS) {
RtlLeaveCriticalSection(&LdrpLoaderLock);
} else {
__try {
RtlLeaveCriticalSection(&LdrpLoaderLock);
} __except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
Status = GetExceptionCode();
goto Exit;
}
}
Status = STATUS_SUCCESS;
Exit:
return Status;
}
NTSTATUS
NTAPI
LdrDoesCurrentThreadOwnLoaderLock(
BOOLEAN *DoesOwnLock
)
{
NTSTATUS Status;
PTEB Teb;
if (DoesOwnLock != NULL)
*DoesOwnLock = FALSE;
if (DoesOwnLock == NULL) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
Teb = NtCurrentTeb();
if (LdrpLoaderLock.OwningThread == Teb->ClientId.UniqueThread)
*DoesOwnLock = TRUE;
Status = STATUS_SUCCESS;
Exit:
return Status;
}
NTSTATUS
NTAPI
LdrEnumerateLoadedModules (
ULONG Flags,
PLDR_LOADED_MODULE_ENUMERATION_CALLBACK_FUNCTION CallbackFunction,
PVOID Context
)
{
NTSTATUS Status;
BOOLEAN LoaderLockLocked = FALSE;
PLIST_ENTRY LoadOrderListHead = NULL;
PLIST_ENTRY ListEntry;
BOOLEAN StopEnumeration = FALSE;
PVOID LockCookie = NULL;
if ((Flags != 0) ||
(CallbackFunction == NULL)) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
Status = LdrLockLoaderLock(0, NULL, &LockCookie);
if (!NT_SUCCESS(Status))
goto Exit;
LoaderLockLocked = TRUE;
LoadOrderListHead = &PebLdr.InLoadOrderModuleList;
ListEntry = LoadOrderListHead->Flink;
while (ListEntry != LoadOrderListHead) {
__try {
(*CallbackFunction)(
CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks),
Context,
&StopEnumeration);
} __except (LdrpGenericExceptionFilter(GetExceptionInformation(), __FUNCTION__)) {
Status = GetExceptionCode();
goto Exit;
}
if (StopEnumeration)
break;
ListEntry = ListEntry->Flink;
}
Status = LdrUnlockLoaderLock(0, LockCookie);
LoaderLockLocked = FALSE;
if (!NT_SUCCESS(Status)) {
goto Exit;
}
Status = STATUS_SUCCESS;
Exit:
if (LoaderLockLocked) {
NTSTATUS Status2;
Status2 = LdrUnlockLoaderLock(0, LockCookie);
ASSERT(NT_SUCCESS(Status2));
}
return Status;
}
NTSTATUS
NTAPI
LdrAddRefDll(
ULONG Flags,
PVOID DllHandle
)
{
NTSTATUS Status = STATUS_SUCCESS;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry = NULL;
const BOOLEAN InLdrInit = LdrpInLdrInit;
PVOID LockCookie = NULL;
BOOLEAN HoldingLoaderLock = FALSE;
const ULONG ValidFlags = LDR_ADDREF_DLL_PIN;
__try {
if (Flags & ~ValidFlags
) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (!InLdrInit
) {
Status = LdrLockLoaderLock(0, NULL, &LockCookie);
if (!NT_SUCCESS(Status))
goto Exit;
HoldingLoaderLock = TRUE;
}
if (!LdrpCheckForLoadedDllHandle(DllHandle, &LdrDataTableEntry)
) {
Status = STATUS_INVALID_PARAMETER;
goto Exit;
}
if (!RTL_SOFT_VERIFY(LdrDataTableEntry != NULL)
) {
Status = STATUS_INTERNAL_ERROR;
goto Exit;
}
//
// Gross. Everyone inlines the first part..
//
if (LdrDataTableEntry->LoadCount != 0xffff) {
if (Flags & LDR_ADDREF_DLL_PIN
) {
LdrDataTableEntry->LoadCount = 0xffff;
LdrpPinLoadedDll(LdrDataTableEntry);
} else {
LdrDataTableEntry->LoadCount++;
LdrpReferenceLoadedDll(LdrDataTableEntry);
}
LdrpClearLoadInProgress();
}
Exit:
if (LdrpShouldDbgPrintStatus(Status)
) {
DbgPrint("LDR: "__FUNCTION__"(%p) 0x%08lx\n", DllHandle, Status);
}
} __finally {
if (HoldingLoaderLock) {
LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
HoldingLoaderLock = FALSE;
}
}
return Status;
}
VOID
NTAPI
LdrSetDllManifestProber(
IN PLDR_MANIFEST_PROBER_ROUTINE ManifestProberRoutine
)
{
LdrpManifestProberRoutine = ManifestProberRoutine;
}
NTSTATUS
NTAPI
LdrSetAppCompatDllRedirectionCallback(
IN ULONG Flags,
IN PLDR_APP_COMPAT_DLL_REDIRECTION_CALLBACK_FUNCTION CallbackFunction,
IN PVOID CallbackData
)
/*++
Routine Description:
This routine allows the application compatibility facility to set a callback
function that it can use to redirect DLL loads wherever it wants them to go.
Arguments:
Flags - None defined now; must be zero.
CallbackFunction - Function pointer to function which is called to resolve
path names prior to actually loading the DLL.
CallbackData - PVOID value passed through to the CallbackFunction when it is
called.
Return Value:
NTSTATUS indicating the success/failure of the function.
--*/
{
NTSTATUS st = STATUS_INTERNAL_ERROR;
PVOID LockCookie = NULL;
if (Flags != 0) {
st = STATUS_INVALID_PARAMETER;
goto Exit;
}
LdrLockLoaderLock(LDR_LOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, NULL, &LockCookie);
__try {
LdrpAppCompatDllRedirectionCallbackFunction = CallbackFunction;
LdrpAppCompatDllRedirectionCallbackData = CallbackData;
} __finally {
LdrUnlockLoaderLock(LDR_UNLOCK_LOADER_LOCK_FLAG_RAISE_ON_ERRORS, LockCookie);
}
st = STATUS_SUCCESS;
Exit:
return st;
}
PTEB LdrpTopLevelDllBeingLoadedTeb=NULL;
BOOLEAN
RtlIsThreadWithinLoaderCallout (
VOID
)
{
if (LdrpTopLevelDllBeingLoadedTeb == NtCurrentTeb ()) {
return TRUE;
} else {
return FALSE;
}
}