reactos/dll/ntdll/ldr/ldrutils.c
Mark Jansen 24a56f89ab
Rework apisets to use a table
This removes all fake apiset forwarders,
and handles apisets inside ntdll.
This is not 100% compatible with how windows does it, but it should be good enough for us.
2024-04-27 22:51:34 +02:00

2786 lines
91 KiB
C

/*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS NT User-Mode Library
* FILE: dll/ntdll/ldr/ldrutils.c
* PURPOSE: Internal Loader Utility Functions
* PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
* Aleksey Bragin (aleksey@reactos.org)
*/
/* INCLUDES *****************************************************************/
#include <ntdll.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
PLDR_DATA_TABLE_ENTRY LdrpLoadedDllHandleCache, LdrpGetModuleHandleCache;
BOOLEAN g_ShimsEnabled;
PVOID g_pShimEngineModule;
PVOID g_pfnSE_DllLoaded;
PVOID g_pfnSE_DllUnloaded;
PVOID g_pfnSE_InstallBeforeInit;
PVOID g_pfnSE_InstallAfterInit;
PVOID g_pfnSE_ProcessDying;
/* FUNCTIONS *****************************************************************/
NTSTATUS
NTAPI
LdrpAllocateUnicodeString(IN OUT PUNICODE_STRING StringOut,
IN ULONG Length)
{
/* Sanity checks */
ASSERT(StringOut);
ASSERT(Length <= UNICODE_STRING_MAX_BYTES);
/* Assume failure */
StringOut->Length = 0;
/* Make sure it's not mis-aligned */
if (Length & 1)
{
/* Fail */
StringOut->Buffer = NULL;
StringOut->MaximumLength = 0;
return STATUS_INVALID_PARAMETER;
}
/* Allocate the string*/
StringOut->Buffer = RtlAllocateHeap(LdrpHeap,
0,
Length + sizeof(WCHAR));
if (!StringOut->Buffer)
{
/* Fail */
StringOut->MaximumLength = 0;
return STATUS_NO_MEMORY;
}
/* Null-terminate it */
StringOut->Buffer[Length / sizeof(WCHAR)] = UNICODE_NULL;
/* Check if this is a maximum-sized string */
if (Length != UNICODE_STRING_MAX_BYTES)
{
/* It's not, so set the maximum length to be one char more */
StringOut->MaximumLength = (USHORT)Length + sizeof(UNICODE_NULL);
}
else
{
/* The length is already the maximum possible */
StringOut->MaximumLength = UNICODE_STRING_MAX_BYTES;
}
/* Return success */
return STATUS_SUCCESS;
}
VOID
NTAPI
LdrpFreeUnicodeString(IN PUNICODE_STRING StringIn)
{
ASSERT(StringIn != NULL);
/* If Buffer is not NULL - free it */
if (StringIn->Buffer)
{
RtlFreeHeap(LdrpHeap, 0, StringIn->Buffer);
}
/* Zero it out */
RtlInitEmptyUnicodeString(StringIn, NULL, 0);
}
BOOLEAN
NTAPI
LdrpCallInitRoutine(IN PDLL_INIT_ROUTINE EntryPoint,
IN PVOID BaseAddress,
IN ULONG Reason,
IN PVOID Context)
{
/* Call the entry */
return EntryPoint(BaseAddress, Reason, Context);
}
/* NOTE: This function is broken */
VOID
NTAPI
LdrpUpdateLoadCount3(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
IN ULONG Flags,
OUT PUNICODE_STRING UpdateString)
{
PIMAGE_BOUND_FORWARDER_REF NewImportForwarder;
PIMAGE_BOUND_IMPORT_DESCRIPTOR FirstEntry;
PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundEntry;
PIMAGE_IMPORT_DESCRIPTOR ImportEntry;
PIMAGE_THUNK_DATA FirstThunk;
PLDR_DATA_TABLE_ENTRY Entry;
PUNICODE_STRING ImportNameUnic, RedirectedImportName;
ANSI_STRING ImportNameAnsi;
LPSTR ImportName;
ULONG ImportSize;
NTSTATUS Status;
ULONG i;
BOOLEAN RedirectedDll;
RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_EXTENDED ActCtx;
/* Set up the Act Ctx */
ActCtx.Size = sizeof(ActCtx);
ActCtx.Format = RTL_CALLER_ALLOCATED_ACTIVATION_CONTEXT_STACK_FRAME_FORMAT_WHISTLER;
RtlZeroMemory(&ActCtx.Frame, sizeof(RTL_ACTIVATION_CONTEXT_STACK_FRAME));
/* Activate the ActCtx */
RtlActivateActivationContextUnsafeFast(&ActCtx,
LdrEntry->EntryPointActivationContext);
/* Check the action we need to perform */
if ((Flags == LDRP_UPDATE_REFCOUNT) || (Flags == LDRP_UPDATE_PIN))
{
/* Make sure entry is not being loaded already */
if (LdrEntry->Flags & LDRP_LOAD_IN_PROGRESS)
goto done;
LdrEntry->Flags |= LDRP_LOAD_IN_PROGRESS;
}
else if (Flags == LDRP_UPDATE_DEREFCOUNT)
{
/* Make sure the entry is not being unloaded already */
if (LdrEntry->Flags & LDRP_UNLOAD_IN_PROGRESS)
goto done;
LdrEntry->Flags |= LDRP_UNLOAD_IN_PROGRESS;
}
/* Go through all bound DLLs and dereference them */
ImportNameUnic = &NtCurrentTeb()->StaticUnicodeString;
/* Try to get the new import entry */
FirstEntry = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT,
&ImportSize);
if (FirstEntry)
{
/* Set entry flags if refing/derefing */
if (Flags == LDRP_UPDATE_REFCOUNT)
LdrEntry->Flags |= LDRP_LOAD_IN_PROGRESS;
else if (Flags == LDRP_UPDATE_DEREFCOUNT)
LdrEntry->Flags |= LDRP_UNLOAD_IN_PROGRESS;
BoundEntry = FirstEntry;
while (BoundEntry->OffsetModuleName)
{
/* Get pointer to the current import name */
ImportName = (LPSTR)FirstEntry + BoundEntry->OffsetModuleName;
RtlInitAnsiString(&ImportNameAnsi, ImportName);
Status = RtlAnsiStringToUnicodeString(ImportNameUnic, &ImportNameAnsi, FALSE);
if (NT_SUCCESS(Status))
{
RedirectedDll = FALSE;
RedirectedImportName = ImportNameUnic;
/* Check if the SxS Assemblies specify another file */
Status = LdrpApplyFileNameRedirection(
ImportNameUnic, &LdrApiDefaultExtension, UpdateString, NULL, &RedirectedImportName,
&RedirectedDll);
/* Check success */
if (NT_SUCCESS(Status) && RedirectedDll)
{
if (ShowSnaps)
{
DPRINT1("LDR: %Z got redirected to %wZ\n", &ImportNameAnsi, RedirectedImportName);
}
}
if (NT_SUCCESS(Status))
{
if (LdrpCheckForLoadedDll(NULL,
RedirectedImportName,
TRUE,
RedirectedDll,
&Entry))
{
if (Entry->LoadCount != 0xFFFF)
{
/* Perform the required action */
switch (Flags)
{
case LDRP_UPDATE_REFCOUNT:
Entry->LoadCount++;
break;
case LDRP_UPDATE_DEREFCOUNT:
Entry->LoadCount--;
break;
case LDRP_UPDATE_PIN:
Entry->LoadCount = 0xFFFF;
break;
}
/* Show snaps */
if (ShowSnaps)
{
DPRINT1("LDR: Flags %lu %wZ (%lx)\n", Flags, RedirectedImportName, Entry->LoadCount);
}
}
/* Recurse into this entry */
LdrpUpdateLoadCount3(Entry, Flags, UpdateString);
}
else if (RedirectedDll)
{
DPRINT1("LDR: LdrpCheckForLoadedDll failed for redirected dll %wZ\n", RedirectedImportName);
}
}
else
{
/* Unrecoverable SxS failure */
DPRINT1("LDR: LdrpApplyFileNameRedirection failed with status %x for dll %wZ\n", Status, ImportNameUnic);
}
}
/* Go through forwarders */
NewImportForwarder = (PIMAGE_BOUND_FORWARDER_REF)(BoundEntry + 1);
for (i = 0; i < BoundEntry->NumberOfModuleForwarderRefs; i++)
{
ImportName = (LPSTR)FirstEntry + NewImportForwarder->OffsetModuleName;
RtlInitAnsiString(&ImportNameAnsi, ImportName);
Status = RtlAnsiStringToUnicodeString(ImportNameUnic, &ImportNameAnsi, FALSE);
if (NT_SUCCESS(Status))
{
RedirectedDll = FALSE;
RedirectedImportName = ImportNameUnic;
/* Check if the SxS Assemblies specify another file */
Status = LdrpApplyFileNameRedirection(
ImportNameUnic, &LdrApiDefaultExtension, UpdateString, NULL, &RedirectedImportName,
&RedirectedDll);
/* Check success */
if (NT_SUCCESS(Status) && RedirectedDll)
{
if (ShowSnaps)
{
DPRINT1("LDR: %Z got redirected to %wZ\n", &ImportNameAnsi, RedirectedImportName);
}
}
if (NT_SUCCESS(Status))
{
if (LdrpCheckForLoadedDll(NULL,
RedirectedImportName,
TRUE,
RedirectedDll,
&Entry))
{
if (Entry->LoadCount != 0xFFFF)
{
/* Perform the required action */
switch (Flags)
{
case LDRP_UPDATE_REFCOUNT:
Entry->LoadCount++;
break;
case LDRP_UPDATE_DEREFCOUNT:
Entry->LoadCount--;
break;
case LDRP_UPDATE_PIN:
Entry->LoadCount = 0xFFFF;
break;
}
/* Show snaps */
if (ShowSnaps)
{
DPRINT1("LDR: Flags %lu %wZ (%lx)\n", Flags, RedirectedImportName, Entry->LoadCount);
}
}
/* Recurse into this entry */
LdrpUpdateLoadCount3(Entry, Flags, UpdateString);
}
else if (RedirectedDll)
{
DPRINT1("LDR: LdrpCheckForLoadedDll failed with status %x for redirected dll %wZ\n", Status, RedirectedImportName);
}
}
else
{
/* Unrecoverable SxS failure */
DPRINT1("LDR: LdrpApplyFileNameRedirection failed with status %x for dll %wZ\n", Status, ImportNameUnic);
}
}
NewImportForwarder++;
}
BoundEntry = (PIMAGE_BOUND_IMPORT_DESCRIPTOR)NewImportForwarder;
}
/* We're done */
goto done;
}
/* Check oldstyle import descriptor */
ImportEntry = (PIMAGE_IMPORT_DESCRIPTOR)RtlImageDirectoryEntryToData(LdrEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_IMPORT,
&ImportSize);
if (ImportEntry)
{
/* There is old one, so go through its entries */
while (ImportEntry->Name && ImportEntry->FirstThunk)
{
FirstThunk = (PIMAGE_THUNK_DATA)((ULONG_PTR)LdrEntry->DllBase + ImportEntry->FirstThunk);
/* Skip this entry if needed */
if (!FirstThunk->u1.Function)
{
ImportEntry++;
continue;
}
ImportName = (PSZ)((ULONG_PTR)LdrEntry->DllBase + ImportEntry->Name);
RtlInitAnsiString(&ImportNameAnsi, ImportName);
Status = RtlAnsiStringToUnicodeString(ImportNameUnic, &ImportNameAnsi, FALSE);
if (NT_SUCCESS(Status))
{
RedirectedDll = FALSE;
RedirectedImportName = ImportNameUnic;
/* Check if the SxS Assemblies specify another file */
Status = LdrpApplyFileNameRedirection(
ImportNameUnic, &LdrApiDefaultExtension, UpdateString, NULL, &RedirectedImportName, &RedirectedDll);
/* Check success */
if (NT_SUCCESS(Status) && RedirectedDll)
{
if (ShowSnaps)
{
DPRINT1("LDR: %Z got redirected to %wZ\n", &ImportNameAnsi, RedirectedImportName);
}
}
if (NT_SUCCESS(Status))
{
if (LdrpCheckForLoadedDll(NULL,
RedirectedImportName,
TRUE,
RedirectedDll,
&Entry))
{
if (Entry->LoadCount != 0xFFFF)
{
/* Perform the required action */
switch (Flags)
{
case LDRP_UPDATE_REFCOUNT:
Entry->LoadCount++;
break;
case LDRP_UPDATE_DEREFCOUNT:
Entry->LoadCount--;
break;
case LDRP_UPDATE_PIN:
Entry->LoadCount = 0xFFFF;
break;
}
/* Show snaps */
if (ShowSnaps)
{
DPRINT1("LDR: Flags %lu %wZ (%lx)\n", Flags, RedirectedImportName, Entry->LoadCount);
}
}
/* Recurse */
LdrpUpdateLoadCount3(Entry, Flags, UpdateString);
}
else if (RedirectedDll)
{
DPRINT1("LDR: LdrpCheckForLoadedDll failed for redirected dll %wZ\n", RedirectedImportName);
}
}
else
{
/* Unrecoverable SxS failure */
DPRINT1("LDR: LdrpApplyFileNameRedirection failed for dll %wZ\n", ImportNameUnic);
}
}
/* Go to the next entry */
ImportEntry++;
}
}
done:
/* Release the context */
RtlDeactivateActivationContextUnsafeFast(&ActCtx);
}
VOID
NTAPI
LdrpUpdateLoadCount2(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
IN ULONG Flags)
{
WCHAR Buffer[MAX_PATH];
UNICODE_STRING UpdateString;
/* Setup the string and call the extended API */
RtlInitEmptyUnicodeString(&UpdateString, Buffer, sizeof(Buffer));
LdrpUpdateLoadCount3(LdrEntry, Flags, &UpdateString);
}
VOID
NTAPI
LdrpCallTlsInitializers(IN PLDR_DATA_TABLE_ENTRY LdrEntry,
IN ULONG Reason)
{
PIMAGE_TLS_DIRECTORY TlsDirectory;
PIMAGE_TLS_CALLBACK *Array, Callback;
ULONG Size;
/* Get the TLS Directory */
TlsDirectory = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_TLS,
&Size);
/* Protect against invalid pointers */
_SEH2_TRY
{
/* Make sure it's valid */
if (TlsDirectory)
{
/* Get the array */
Array = (PIMAGE_TLS_CALLBACK *)TlsDirectory->AddressOfCallBacks;
if (Array)
{
/* Display debug */
if (ShowSnaps)
{
DPRINT1("LDR: Tls Callbacks Found. Imagebase %p Tls %p CallBacks %p\n",
LdrEntry->DllBase, TlsDirectory, Array);
}
/* Loop the array */
while (*Array)
{
/* Get the TLS Entrypoint */
Callback = *Array++;
/* Display debug */
if (ShowSnaps)
{
DPRINT1("LDR: Calling Tls Callback Imagebase %p Function %p\n",
LdrEntry->DllBase, Callback);
}
/* Call it */
LdrpCallInitRoutine((PDLL_INIT_ROUTINE)Callback,
LdrEntry->DllBase,
Reason,
NULL);
}
}
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPRINT1("LDR: Exception 0x%x during Tls Callback(%u) for %wZ\n",
_SEH2_GetExceptionCode(), Reason, &LdrEntry->BaseDllName);
}
_SEH2_END;
}
NTSTATUS
NTAPI
LdrpCodeAuthzCheckDllAllowed(IN PUNICODE_STRING FullName,
IN HANDLE DllHandle)
{
/* Not implemented */
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
LdrpCreateDllSection(IN PUNICODE_STRING FullName,
IN HANDLE DllHandle,
IN PULONG DllCharacteristics OPTIONAL,
OUT PHANDLE SectionHandle)
{
HANDLE FileHandle;
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
IO_STATUS_BLOCK IoStatusBlock;
ULONG_PTR HardErrorParameters[1];
ULONG Response;
SECTION_IMAGE_INFORMATION SectionImageInfo;
/* Check if we don't already have a handle */
if (!DllHandle)
{
/* Create the object attributes */
InitializeObjectAttributes(&ObjectAttributes,
FullName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
/* Open the DLL */
Status = NtOpenFile(&FileHandle,
SYNCHRONIZE | FILE_EXECUTE | FILE_READ_DATA,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
/* Check if we failed */
if (!NT_SUCCESS(Status))
{
/* Attempt to open for execute only */
Status = NtOpenFile(&FileHandle,
SYNCHRONIZE | FILE_EXECUTE,
&ObjectAttributes,
&IoStatusBlock,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
/* Check if this failed too */
if (!NT_SUCCESS(Status))
{
/* Show debug message */
if (ShowSnaps)
{
DPRINT1("LDR: LdrpCreateDllSection - NtOpenFile failed; status = %x\n",
Status);
}
/* Make sure to return an expected status code */
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
/* Callers expect this instead */
Status = STATUS_DLL_NOT_FOUND;
}
/* Return an empty section handle */
*SectionHandle = NULL;
return Status;
}
}
}
else
{
/* Use the handle we already have */
FileHandle = DllHandle;
}
/* Create a section for the DLL */
Status = NtCreateSection(SectionHandle,
SECTION_MAP_READ | SECTION_MAP_EXECUTE |
SECTION_MAP_WRITE | SECTION_QUERY,
NULL,
NULL,
PAGE_EXECUTE,
SEC_IMAGE,
FileHandle);
/* If mapping failed, raise a hard error */
if (!NT_SUCCESS(Status))
{
/* Forget the handle */
*SectionHandle = NULL;
/* Give the DLL name */
HardErrorParameters[0] = (ULONG_PTR)FullName;
/* Raise the error */
ZwRaiseHardError(STATUS_INVALID_IMAGE_FORMAT,
1,
1,
HardErrorParameters,
OptionOk,
&Response);
/* Increment the error count */
if (LdrpInLdrInit) LdrpFatalHardErrorCount++;
goto Exit;
}
/* Check for Safer restrictions */
if (!DllCharacteristics ||
!(*DllCharacteristics & IMAGE_FILE_SYSTEM))
{
/* Make sure it's executable */
Status = ZwQuerySection(*SectionHandle,
SectionImageInformation,
&SectionImageInfo,
sizeof(SECTION_IMAGE_INFORMATION),
NULL);
if (NT_SUCCESS(Status))
{
/* Bypass the check for .NET images */
if (!(SectionImageInfo.LoaderFlags & IMAGE_LOADER_FLAGS_COMPLUS))
{
/* Check with Safer */
Status = LdrpCodeAuthzCheckDllAllowed(FullName, DllHandle);
if (!NT_SUCCESS(Status) && (Status != STATUS_NOT_FOUND))
{
/* Show debug message */
if (ShowSnaps)
{
DPRINT1("LDR: Loading of (%wZ) blocked by Winsafer\n",
&FullName);
}
/* Failure case, close section handle */
NtClose(*SectionHandle);
*SectionHandle = NULL;
}
}
}
else
{
/* Failure case, close section handle */
NtClose(*SectionHandle);
*SectionHandle = NULL;
}
}
Exit:
/* Close the file handle, we don't need it */
NtClose(FileHandle);
/* Return status */
return Status;
}
/* NOTE: This function is totally b0rked and doesn't handle the parameters/functionality it should */
BOOLEAN
NTAPI
LdrpResolveDllName(PWSTR DllPath,
PWSTR DllName,
PUNICODE_STRING FullDllName,
PUNICODE_STRING BaseDllName)
{
PWCHAR NameBuffer, p1, p2 = 0;
ULONG Length;
ULONG BufSize = 500;
/* Allocate space for full DLL name */
FullDllName->Buffer = RtlAllocateHeap(LdrpHeap, 0, BufSize + sizeof(UNICODE_NULL));
if (!FullDllName->Buffer) return FALSE;
Length = RtlDosSearchPath_U(DllPath ? DllPath : LdrpDefaultPath.Buffer,
DllName,
NULL,
BufSize,
FullDllName->Buffer,
&BaseDllName->Buffer);
if (!Length || Length > BufSize)
{
if (ShowSnaps)
{
DPRINT1("LDR: LdrResolveDllName - Unable to find ");
DPRINT1("%ws from %ws\n", DllName, DllPath ? DllPath : LdrpDefaultPath.Buffer);
}
LdrpFreeUnicodeString(FullDllName);
return FALSE;
}
/* Construct full DLL name */
FullDllName->Length = Length;
FullDllName->MaximumLength = FullDllName->Length + sizeof(UNICODE_NULL);
/* Allocate a new buffer */
NameBuffer = RtlAllocateHeap(LdrpHeap, 0, FullDllName->MaximumLength);
if (!NameBuffer)
{
RtlFreeHeap(LdrpHeap, 0, FullDllName->Buffer);
return FALSE;
}
/* Copy over the contents from the previous one and free it */
RtlCopyMemory(NameBuffer, FullDllName->Buffer, FullDllName->MaximumLength);
RtlFreeHeap(LdrpHeap, 0, FullDllName->Buffer);
FullDllName->Buffer = NameBuffer;
/* Find last backslash */
p1 = FullDllName->Buffer;
while (*p1)
{
if (*p1++ == L'\\')
{
p2 = p1;
}
}
/* If found, set p1 to it, otherwise p1 points to the beginning of DllName */
if (p2)
p1 = p2;
else
p1 = DllName;
p2 = p1;
/* Calculate remaining length */
while (*p1) ++p1;
/* Construct base DLL name */
BaseDllName->Length = (ULONG_PTR)p1 - (ULONG_PTR)p2;
BaseDllName->MaximumLength = BaseDllName->Length + sizeof(UNICODE_NULL);
BaseDllName->Buffer = RtlAllocateHeap(LdrpHeap, 0, BaseDllName->MaximumLength);
if (!BaseDllName->Buffer)
{
RtlFreeHeap(LdrpHeap, 0, NameBuffer);
return FALSE;
}
/* Copy base dll name to the new buffer */
RtlMoveMemory(BaseDllName->Buffer,
p2,
BaseDllName->Length);
/* Null-terminate the string */
BaseDllName->Buffer[BaseDllName->Length / sizeof(WCHAR)] = 0;
return TRUE;
}
PVOID
NTAPI
LdrpFetchAddressOfEntryPoint(IN PVOID ImageBase)
{
PIMAGE_NT_HEADERS NtHeaders;
ULONG_PTR EntryPoint = 0;
/* Get entry point offset from NT headers */
NtHeaders = RtlImageNtHeader(ImageBase);
if (NtHeaders)
{
/* Add image base */
EntryPoint = NtHeaders->OptionalHeader.AddressOfEntryPoint;
if (EntryPoint) EntryPoint += (ULONG_PTR)ImageBase;
}
/* Return calculated pointer (or zero in case of failure) */
return (PVOID)EntryPoint;
}
/* NOTE: This function is partially missing SxS */
NTSTATUS
NTAPI
LdrpCheckForKnownDll(PWSTR DllName,
PUNICODE_STRING FullDllName,
PUNICODE_STRING BaseDllName,
HANDLE *SectionHandle)
{
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE Section = NULL;
UNICODE_STRING DllNameUnic;
NTSTATUS Status;
PCHAR p1;
PWCHAR p2;
/* Zero initialize provided parameters */
if (SectionHandle) *SectionHandle = 0;
if (FullDllName)
{
FullDllName->Length = 0;
FullDllName->MaximumLength = 0;
FullDllName->Buffer = NULL;
}
if (BaseDllName)
{
BaseDllName->Length = 0;
BaseDllName->MaximumLength = 0;
BaseDllName->Buffer = NULL;
}
/* If any of these three params are missing then fail */
if (!SectionHandle || !FullDllName || !BaseDllName)
return STATUS_INVALID_PARAMETER;
/* Check the Loader Lock */
LdrpEnsureLoaderLockIsHeld();
/* Upgrade DllName to a unicode string */
RtlInitUnicodeString(&DllNameUnic, DllName);
/* FIXME: Missing RtlComputePrivatizedDllName_U related functionality */
/* Get the activation context */
Status = RtlFindActivationContextSectionString(0,
NULL,
ACTIVATION_CONTEXT_SECTION_DLL_REDIRECTION,
&DllNameUnic,
NULL);
/* Check if it's a SxS or not */
if (Status == STATUS_SXS_SECTION_NOT_FOUND ||
Status == STATUS_SXS_KEY_NOT_FOUND)
{
/* NOTE: Here it's beneficial to allocate one big unicode string
using LdrpAllocateUnicodeString instead of fragmenting the heap
with two allocations as it's done now. */
/* Set up BaseDllName */
BaseDllName->Length = DllNameUnic.Length;
BaseDllName->MaximumLength = DllNameUnic.MaximumLength;
BaseDllName->Buffer = RtlAllocateHeap(LdrpHeap,
0,
DllNameUnic.MaximumLength);
if (!BaseDllName->Buffer)
{
Status = STATUS_NO_MEMORY;
goto Failure;
}
/* Copy the contents there */
RtlMoveMemory(BaseDllName->Buffer, DllNameUnic.Buffer, DllNameUnic.MaximumLength);
/* Set up FullDllName */
FullDllName->Length = LdrpKnownDllPath.Length + BaseDllName->Length + sizeof(WCHAR);
FullDllName->MaximumLength = FullDllName->Length + sizeof(UNICODE_NULL);
FullDllName->Buffer = RtlAllocateHeap(LdrpHeap, 0, FullDllName->MaximumLength);
if (!FullDllName->Buffer)
{
Status = STATUS_NO_MEMORY;
goto Failure;
}
RtlMoveMemory(FullDllName->Buffer, LdrpKnownDllPath.Buffer, LdrpKnownDllPath.Length);
/* Put a slash there */
p1 = (PCHAR)FullDllName->Buffer + LdrpKnownDllPath.Length;
p2 = (PWCHAR)p1;
*p2++ = (WCHAR)'\\';
p1 = (PCHAR)p2;
/* Set up DllNameUnic for a relative path */
DllNameUnic.Buffer = (PWSTR)p1;
DllNameUnic.Length = BaseDllName->Length;
DllNameUnic.MaximumLength = DllNameUnic.Length + sizeof(UNICODE_NULL);
/* Copy the contents */
RtlMoveMemory(p1, BaseDllName->Buffer, BaseDllName->MaximumLength);
/* There are all names, init attributes and open the section */
InitializeObjectAttributes(&ObjectAttributes,
&DllNameUnic,
OBJ_CASE_INSENSITIVE,
LdrpKnownDllObjectDirectory,
NULL);
Status = NtOpenSection(&Section,
SECTION_MAP_READ | SECTION_MAP_EXECUTE | SECTION_MAP_WRITE,
&ObjectAttributes);
if (!NT_SUCCESS(Status))
{
/* Clear status in case it was just not found */
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) Status = STATUS_SUCCESS;
goto Failure;
}
/* Pass section handle to the caller and return success */
*SectionHandle = Section;
return STATUS_SUCCESS;
}
Failure:
/* Close section object if it was opened */
if (Section) NtClose(Section);
/* Free string resources */
if (BaseDllName->Buffer) RtlFreeHeap(LdrpHeap, 0, BaseDllName->Buffer);
if (FullDllName->Buffer) RtlFreeHeap(LdrpHeap, 0, FullDllName->Buffer);
/* Return status */
return Status;
}
NTSTATUS
NTAPI
LdrpSetProtection(PVOID ViewBase,
BOOLEAN Restore)
{
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_SECTION_HEADER Section;
NTSTATUS Status;
PVOID SectionBase;
SIZE_T SectionSize;
ULONG NewProtection, OldProtection, i;
/* Get the NT headers */
NtHeaders = RtlImageNtHeader(ViewBase);
if (!NtHeaders) return STATUS_INVALID_IMAGE_FORMAT;
/* Compute address of the first section header */
Section = IMAGE_FIRST_SECTION(NtHeaders);
/* Go through all sections */
for (i = 0; i < NtHeaders->FileHeader.NumberOfSections; i++)
{
/* Check for read-only non-zero section */
if ((Section->SizeOfRawData) &&
!(Section->Characteristics & IMAGE_SCN_MEM_WRITE))
{
/* Check if we are setting or restoring protection */
if (Restore)
{
/* Set it to either EXECUTE or READONLY */
if (Section->Characteristics & IMAGE_SCN_MEM_EXECUTE)
{
NewProtection = PAGE_EXECUTE;
}
else
{
NewProtection = PAGE_READONLY;
}
/* Add PAGE_NOCACHE if needed */
if (Section->Characteristics & IMAGE_SCN_MEM_NOT_CACHED)
{
NewProtection |= PAGE_NOCACHE;
}
}
else
{
/* Enable write access */
NewProtection = PAGE_READWRITE;
}
/* Get the section VA */
SectionBase = (PVOID)((ULONG_PTR)ViewBase + Section->VirtualAddress);
SectionSize = Section->SizeOfRawData;
if (SectionSize)
{
/* Set protection */
Status = ZwProtectVirtualMemory(NtCurrentProcess(),
&SectionBase,
&SectionSize,
NewProtection,
&OldProtection);
if (!NT_SUCCESS(Status)) return Status;
}
}
/* Move to the next section */
Section++;
}
/* Flush instruction cache if necessary */
if (Restore) ZwFlushInstructionCache(NtCurrentProcess(), NULL, 0);
return STATUS_SUCCESS;
}
/* NOTE: Not yet reviewed */
NTSTATUS
NTAPI
LdrpMapDll(IN PWSTR SearchPath OPTIONAL,
IN PWSTR DllPath2,
IN PWSTR DllName OPTIONAL,
IN PULONG DllCharacteristics,
IN BOOLEAN Static,
IN BOOLEAN Redirect,
OUT PLDR_DATA_TABLE_ENTRY *DataTableEntry)
{
PTEB Teb = NtCurrentTeb();
PPEB Peb = NtCurrentPeb();
PWCHAR p1 = DllName;
WCHAR TempChar;
BOOLEAN KnownDll = FALSE;
UNICODE_STRING FullDllName, BaseDllName;
HANDLE SectionHandle = NULL, DllHandle = 0;
UNICODE_STRING NtPathDllName;
ULONG_PTR HardErrorParameters[2];
UNICODE_STRING HardErrorDllName, HardErrorDllPath;
ULONG Response;
SIZE_T ViewSize = 0;
PVOID ViewBase = NULL;
PVOID ArbitraryUserPointer;
PIMAGE_NT_HEADERS NtHeaders;
NTSTATUS HardErrorStatus, Status;
BOOLEAN OverlapDllFound = FALSE;
ULONG_PTR ImageBase, ImageEnd;
PLIST_ENTRY ListHead, NextEntry;
PLDR_DATA_TABLE_ENTRY CandidateEntry, LdrEntry;
ULONG_PTR CandidateBase, CandidateEnd;
UNICODE_STRING OverlapDll;
BOOLEAN RelocatableDll = TRUE;
UNICODE_STRING IllegalDll;
PVOID RelocData;
ULONG RelocDataSize = 0;
// FIXME: AppCompat stuff is missing
if (ShowSnaps)
{
DPRINT1("LDR: LdrpMapDll: Image Name %ws, Search Path %ws\n",
DllName,
SearchPath ? SearchPath : L"");
}
/* Check if we have a known dll directory */
if (LdrpKnownDllObjectDirectory && Redirect == FALSE)
{
/* Check if the path is full */
while (*p1)
{
TempChar = *p1++;
if (TempChar == '\\' || TempChar == '/' )
{
/* Complete path, don't do Known Dll lookup */
goto SkipCheck;
}
}
/* Try to find a Known DLL */
Status = LdrpCheckForKnownDll(DllName,
&FullDllName,
&BaseDllName,
&SectionHandle);
if (!NT_SUCCESS(Status) && (Status != STATUS_DLL_NOT_FOUND))
{
/* Failure */
DbgPrintEx(DPFLTR_LDR_ID,
DPFLTR_ERROR_LEVEL,
"LDR: %s - call to LdrpCheckForKnownDll(\"%ws\", ...) failed with status %x\n",
__FUNCTION__,
DllName,
Status);
return Status;
}
}
SkipCheck:
/* Check if the Known DLL Check returned something */
if (!SectionHandle)
{
/* It didn't, so try to resolve the name now */
if (LdrpResolveDllName(SearchPath,
DllName,
&FullDllName,
&BaseDllName))
{
/* Got a name, display a message */
if (ShowSnaps)
{
DPRINT1("LDR: Loading (%s) %wZ\n",
Static ? "STATIC" : "DYNAMIC",
&FullDllName);
}
/* Convert to NT Name */
if (!RtlDosPathNameToNtPathName_U(FullDllName.Buffer,
&NtPathDllName,
NULL,
NULL))
{
/* Path was invalid */
return STATUS_OBJECT_PATH_SYNTAX_BAD;
}
/* Create a section for this dLL */
Status = LdrpCreateDllSection(&NtPathDllName,
DllHandle,
DllCharacteristics,
&SectionHandle);
/* Free the NT Name */
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathDllName.Buffer);
/* If we failed */
if (!NT_SUCCESS(Status))
{
/* Free the name strings and return */
LdrpFreeUnicodeString(&FullDllName);
LdrpFreeUnicodeString(&BaseDllName);
return Status;
}
}
else
{
/* We couldn't resolve the name, is this a static load? */
if (Static)
{
/*
* This is BAD! Static loads are CRITICAL. Bugcheck!
* Initialize the strings for the error
*/
RtlInitUnicodeString(&HardErrorDllName, DllName);
RtlInitUnicodeString(&HardErrorDllPath,
DllPath2 ? DllPath2 : LdrpDefaultPath.Buffer);
/* Set them as error parameters */
HardErrorParameters[0] = (ULONG_PTR)&HardErrorDllName;
HardErrorParameters[1] = (ULONG_PTR)&HardErrorDllPath;
/* Raise the hard error */
NtRaiseHardError(STATUS_DLL_NOT_FOUND,
2,
0x00000003,
HardErrorParameters,
OptionOk,
&Response);
/* We're back, where we initializing? */
if (LdrpInLdrInit) LdrpFatalHardErrorCount++;
}
/* Return failure */
return STATUS_DLL_NOT_FOUND;
}
}
else
{
/* We have a section handle, so this is a known dll */
KnownDll = TRUE;
}
/* Stuff the image name in the TIB, for the debugger */
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
Teb->NtTib.ArbitraryUserPointer = FullDllName.Buffer;
/* Map the DLL */
ViewBase = NULL;
ViewSize = 0;
Status = NtMapViewOfSection(SectionHandle,
NtCurrentProcess(),
&ViewBase,
0,
0,
NULL,
&ViewSize,
ViewShare,
0,
PAGE_READWRITE);
/* Restore */
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
/* Fail if we couldn't map it */
if (!NT_SUCCESS(Status))
{
/* Close and return */
NtClose(SectionHandle);
return Status;
}
/* Get the NT Header */
if (!(NtHeaders = RtlImageNtHeader(ViewBase)))
{
/* Invalid image, unmap, close handle and fail */
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
NtClose(SectionHandle);
return STATUS_INVALID_IMAGE_FORMAT;
}
// FIXME: .NET support is missing
/* Allocate an entry */
if (!(LdrEntry = LdrpAllocateDataTableEntry(ViewBase)))
{
/* Invalid image, unmap, close handle and fail */
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
NtClose(SectionHandle);
return STATUS_NO_MEMORY;
}
/* Setup the entry */
LdrEntry->Flags = Static ? LDRP_STATIC_LINK : 0;
if (Redirect) LdrEntry->Flags |= LDRP_REDIRECTED;
LdrEntry->LoadCount = 0;
LdrEntry->FullDllName = FullDllName;
LdrEntry->BaseDllName = BaseDllName;
LdrEntry->EntryPoint = LdrpFetchAddressOfEntryPoint(LdrEntry->DllBase);
/* Show debug message */
if (ShowSnaps)
{
DPRINT1("LDR: LdrpMapDll: Full Name %wZ, Base Name %wZ\n",
&FullDllName,
&BaseDllName);
}
/* Insert this entry */
LdrpInsertMemoryTableEntry(LdrEntry);
// LdrpSendDllNotifications(LdrEntry, TRUE, Status == STATUS_IMAGE_NOT_AT_BASE)
/* Check for invalid CPU Image */
if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
{
/* Load our header */
PIMAGE_NT_HEADERS ImageNtHeader = RtlImageNtHeader(Peb->ImageBaseAddress);
/* Assume defaults if we don't have to run the Hard Error path */
HardErrorStatus = STATUS_SUCCESS;
Response = ResponseCancel;
/* Are we an NT 3.0 image? [Do these still exist? LOL -- IAI] */
if (ImageNtHeader->OptionalHeader.MajorSubsystemVersion <= 3)
{
/* Reset the entrypoint, save our Dll Name */
LdrEntry->EntryPoint = 0;
HardErrorParameters[0] = (ULONG_PTR)&FullDllName;
/* Raise the error */
HardErrorStatus = ZwRaiseHardError(STATUS_IMAGE_MACHINE_TYPE_MISMATCH,
1,
1,
HardErrorParameters,
OptionOkCancel,
&Response);
}
/* Check if the user pressed cancel */
if (NT_SUCCESS(HardErrorStatus) && Response == ResponseCancel)
{
/* Remove the DLL from the lists */
RemoveEntryList(&LdrEntry->InLoadOrderLinks);
RemoveEntryList(&LdrEntry->InMemoryOrderLinks);
RemoveEntryList(&LdrEntry->HashLinks);
/* Remove the LDR Entry */
RtlFreeHeap(LdrpHeap, 0, LdrEntry );
/* Unmap and close section */
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
NtClose(SectionHandle);
/* Did we do a hard error? */
if (ImageNtHeader->OptionalHeader.MajorSubsystemVersion <= 3)
{
/* Yup, so increase fatal error count if we are initializing */
if (LdrpInLdrInit) LdrpFatalHardErrorCount++;
}
/* Return failure */
return STATUS_INVALID_IMAGE_FORMAT;
}
}
else
{
/* The image was valid. Is it a DLL? */
if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL)
{
/* Set the DLL Flag */
LdrEntry->Flags |= LDRP_IMAGE_DLL;
}
/* If we're not a DLL, clear the entrypoint */
if (!(LdrEntry->Flags & LDRP_IMAGE_DLL))
{
LdrEntry->EntryPoint = 0;
}
}
/* Return it for the caller */
*DataTableEntry = LdrEntry;
/* Check if we loaded somewhere else */
if (Status == STATUS_IMAGE_NOT_AT_BASE)
{
/* Write the flag */
LdrEntry->Flags |= LDRP_IMAGE_NOT_AT_BASE;
/* Find our region */
ImageBase = (ULONG_PTR)NtHeaders->OptionalHeader.ImageBase;
ImageEnd = ImageBase + ViewSize;
DPRINT("LDR: LdrpMapDll Relocating Image Name %ws (%p-%p -> %p)\n", DllName, (PVOID)ImageBase, (PVOID)ImageEnd, ViewBase);
/* Scan all the modules */
ListHead = &Peb->Ldr->InLoadOrderModuleList;
NextEntry = ListHead->Flink;
while (NextEntry != ListHead)
{
/* Get the entry */
CandidateEntry = CONTAINING_RECORD(NextEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
NextEntry = NextEntry->Flink;
/* Get the entry's bounds */
CandidateBase = (ULONG_PTR)CandidateEntry->DllBase;
CandidateEnd = CandidateBase + CandidateEntry->SizeOfImage;
/* Make sure this entry isn't unloading */
if (!CandidateEntry->InMemoryOrderLinks.Flink) continue;
/* Check if our regions are colliding */
if ((ImageBase >= CandidateBase && ImageBase <= CandidateEnd) ||
(ImageEnd >= CandidateBase && ImageEnd <= CandidateEnd) ||
(CandidateBase >= ImageBase && CandidateBase <= ImageEnd))
{
/* Found who is overlapping */
OverlapDllFound = TRUE;
OverlapDll = CandidateEntry->FullDllName;
break;
}
}
/* Check if we found the DLL overlapping with us */
if (!OverlapDllFound)
{
/* It's not another DLL, it's memory already here */
RtlInitUnicodeString(&OverlapDll, L"Dynamically Allocated Memory");
}
DPRINT("Overlapping DLL: %wZ\n", &OverlapDll);
/* Are we dealing with a DLL? */
if (LdrEntry->Flags & LDRP_IMAGE_DLL)
{
/* Check if relocs were stripped */
if (!(NtHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED))
{
/* Get the relocation data */
RelocData = RtlImageDirectoryEntryToData(ViewBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_BASERELOC,
&RelocDataSize);
/* Does the DLL not have any? */
if (!RelocData && !RelocDataSize)
{
/* We'll allow this and simply continue */
goto NoRelocNeeded;
}
}
/* See if this is an Illegal DLL - IE: user32 and kernel32 */
RtlInitUnicodeString(&IllegalDll,L"user32.dll");
if (RtlEqualUnicodeString(&BaseDllName, &IllegalDll, TRUE))
{
/* Can't relocate user32 */
RelocatableDll = FALSE;
}
else
{
RtlInitUnicodeString(&IllegalDll, L"kernel32.dll");
if (RtlEqualUnicodeString(&BaseDllName, &IllegalDll, TRUE))
{
/* Can't relocate kernel32 */
RelocatableDll = FALSE;
}
}
/* Known DLLs are not allowed to be relocated */
if (KnownDll && !RelocatableDll)
{
/* Setup for hard error */
HardErrorParameters[0] = (ULONG_PTR)&IllegalDll;
HardErrorParameters[1] = (ULONG_PTR)&OverlapDll;
DPRINT1("Illegal DLL relocation! %wZ overlaps %wZ\n", &OverlapDll, &IllegalDll);
/* Raise the error */
ZwRaiseHardError(STATUS_ILLEGAL_DLL_RELOCATION,
2,
3,
HardErrorParameters,
OptionOk,
&Response);
/* If initializing, increase the error count */
if (LdrpInLdrInit) LdrpFatalHardErrorCount++;
/* Don't do relocation */
Status = STATUS_CONFLICTING_ADDRESSES;
goto FailRelocate;
}
/* Change the protection to prepare for relocation */
Status = LdrpSetProtection(ViewBase, FALSE);
/* Make sure we changed the protection */
if (NT_SUCCESS(Status))
{
/* Do the relocation */
Status = LdrRelocateImageWithBias(ViewBase, 0LL, NULL, STATUS_SUCCESS,
STATUS_CONFLICTING_ADDRESSES, STATUS_INVALID_IMAGE_FORMAT);
if (NT_SUCCESS(Status))
{
/* Stuff the image name in the TIB, for the debugger */
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
Teb->NtTib.ArbitraryUserPointer = FullDllName.Buffer;
#if 0
/* Map the DLL */
Status = NtMapViewOfSection(SectionHandle,
NtCurrentProcess(),
&ViewBase,
0,
0,
NULL,
&ViewSize,
ViewShare,
0,
PAGE_READWRITE);
#endif
/* Restore */
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
/* Return the protection */
Status = LdrpSetProtection(ViewBase, TRUE);
}
}
FailRelocate:
/* Handle any kind of failure */
if (!NT_SUCCESS(Status))
{
/* Remove it from the lists */
RemoveEntryList(&LdrEntry->InLoadOrderLinks);
RemoveEntryList(&LdrEntry->InMemoryOrderLinks);
RemoveEntryList(&LdrEntry->HashLinks);
/* Unmap it, clear the entry */
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
LdrEntry = NULL;
}
/* Show debug message */
if (ShowSnaps)
{
DPRINT1("LDR: Fixups %successfully re-applied @ %p\n",
NT_SUCCESS(Status) ? "s" : "uns", ViewBase);
}
}
else
{
NoRelocNeeded:
/* Not a DLL, or no relocation needed */
Status = STATUS_SUCCESS;
/* Stuff the image name in the TIB, for the debugger */
ArbitraryUserPointer = Teb->NtTib.ArbitraryUserPointer;
Teb->NtTib.ArbitraryUserPointer = FullDllName.Buffer;
#if 0
/* Map the DLL */
Status = NtMapViewOfSection(SectionHandle,
NtCurrentProcess(),
&ViewBase,
0,
0,
NULL,
&ViewSize,
ViewShare,
0,
PAGE_READWRITE);
#endif
/* Restore */
Teb->NtTib.ArbitraryUserPointer = ArbitraryUserPointer;
/* Show debug message */
if (ShowSnaps)
{
DPRINT1("LDR: Fixups won't be re-applied to non-Dll @ %p\n", ViewBase);
}
}
}
// FIXME: LdrpCheckCorImage() is missing
/* Check if this is an SMP Machine and a DLL */
if ((LdrpNumberOfProcessors > 1) &&
(LdrEntry && (LdrEntry->Flags & LDRP_IMAGE_DLL)))
{
/* Validate the image for MP */
LdrpValidateImageForMp(LdrEntry);
}
// FIXME: LdrpCorUnloadImage() is missing
/* Close section and return status */
NtClose(SectionHandle);
return Status;
}
PLDR_DATA_TABLE_ENTRY
NTAPI
LdrpAllocateDataTableEntry(IN PVOID BaseAddress)
{
PLDR_DATA_TABLE_ENTRY LdrEntry = NULL;
PIMAGE_NT_HEADERS NtHeader;
/* Make sure the header is valid */
NtHeader = RtlImageNtHeader(BaseAddress);
DPRINT("LdrpAllocateDataTableEntry(%p), NtHeader %p\n", BaseAddress, NtHeader);
if (NtHeader)
{
/* Allocate an entry */
LdrEntry = RtlAllocateHeap(LdrpHeap,
HEAP_ZERO_MEMORY,
sizeof(LDR_DATA_TABLE_ENTRY));
/* Make sure we got one */
if (LdrEntry)
{
/* Set it up */
LdrEntry->DllBase = BaseAddress;
LdrEntry->SizeOfImage = NtHeader->OptionalHeader.SizeOfImage;
LdrEntry->TimeDateStamp = NtHeader->FileHeader.TimeDateStamp;
LdrEntry->PatchInformation = NULL;
}
}
/* Return the entry */
return LdrEntry;
}
VOID
NTAPI
LdrpInsertMemoryTableEntry(IN PLDR_DATA_TABLE_ENTRY LdrEntry)
{
PPEB_LDR_DATA PebData = NtCurrentPeb()->Ldr;
ULONG i;
/* Insert into hash table */
i = LDR_GET_HASH_ENTRY(LdrEntry->BaseDllName.Buffer[0]);
InsertTailList(&LdrpHashTable[i], &LdrEntry->HashLinks);
/* Insert into other lists */
InsertTailList(&PebData->InLoadOrderModuleList, &LdrEntry->InLoadOrderLinks);
InsertTailList(&PebData->InMemoryOrderModuleList, &LdrEntry->InMemoryOrderLinks);
}
VOID
NTAPI
LdrpFinalizeAndDeallocateDataTableEntry(IN PLDR_DATA_TABLE_ENTRY Entry)
{
/* Sanity check */
ASSERT(Entry != NULL);
/* Release the activation context if it exists and wasn't already released */
if ((Entry->EntryPointActivationContext) &&
(Entry->EntryPointActivationContext != INVALID_HANDLE_VALUE))
{
/* Mark it as invalid */
RtlReleaseActivationContext(Entry->EntryPointActivationContext);
Entry->EntryPointActivationContext = INVALID_HANDLE_VALUE;
}
/* Release the full dll name string */
if (Entry->FullDllName.Buffer) LdrpFreeUnicodeString(&Entry->FullDllName);
/* Finally free the entry's memory */
RtlFreeHeap(LdrpHeap, 0, Entry);
}
BOOLEAN
NTAPI
LdrpCheckForLoadedDllHandle(IN PVOID Base,
OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
{
PLDR_DATA_TABLE_ENTRY Current;
PLIST_ENTRY ListHead, Next;
/* Check the cache first */
if ((LdrpLoadedDllHandleCache) &&
(LdrpLoadedDllHandleCache->DllBase == Base))
{
/* We got lucky, return the cached entry */
*LdrEntry = LdrpLoadedDllHandleCache;
return TRUE;
}
/* Time for a lookup */
ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
Next = ListHead->Flink;
while (Next != ListHead)
{
/* Get the current entry */
Current = CONTAINING_RECORD(Next,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
/* Make sure it's not unloading and check for a match */
if ((Current->InMemoryOrderLinks.Flink) && (Base == Current->DllBase))
{
/* Save in cache */
LdrpLoadedDllHandleCache = Current;
/* Return it */
*LdrEntry = Current;
return TRUE;
}
/* Move to the next one */
Next = Next->Flink;
}
/* Nothing found */
return FALSE;
}
NTSTATUS
NTAPI
LdrpResolveFullName(IN PUNICODE_STRING OriginalName,
IN PUNICODE_STRING PathName,
IN PUNICODE_STRING FullPathName,
IN PUNICODE_STRING *ExpandedName)
{
NTSTATUS Status = STATUS_SUCCESS;
// RTL_PATH_TYPE PathType;
// BOOLEAN InvalidName;
ULONG Length;
/* Display debug output if snaps are on */
if (ShowSnaps)
{
DbgPrintEx(DPFLTR_LDR_ID,
DPFLTR_ERROR_LEVEL,
"LDR: %s - Expanding full name of %wZ\n",
__FUNCTION__,
OriginalName);
}
/* FIXME: Lock the PEB */
//RtlEnterCriticalSection(&FastPebLock);
#if 0
/* Get the path name */
Length = RtlGetFullPathName_Ustr(OriginalName,
PathName->Length,
PathName->Buffer,
NULL,
&InvalidName,
&PathType);
#else
Length = 0;
#endif
if (!(Length) || (Length > UNICODE_STRING_MAX_BYTES))
{
/* Fail */
Status = STATUS_NAME_TOO_LONG;
goto Quickie;
}
/* Check if the length hasn't changed */
if (Length <= PathName->Length)
{
/* Return the same thing */
*ExpandedName = PathName;
PathName->Length = (USHORT)Length;
goto Quickie;
}
/* Sanity check */
ASSERT(Length >= sizeof(WCHAR));
/* Allocate a string */
Status = LdrpAllocateUnicodeString(FullPathName, Length - sizeof(WCHAR));
if (!NT_SUCCESS(Status)) goto Quickie;
/* Now get the full path again */
#if 0
Length = RtlGetFullPathName_Ustr(OriginalName,
FullPathName->Length,
FullPathName->Buffer,
NULL,
&InvalidName,
&PathType);
#else
Length = 0;
#endif
if (!(Length) || (Length > FullPathName->Length))
{
/* Fail */
LdrpFreeUnicodeString(FullPathName);
Status = STATUS_NAME_TOO_LONG;
}
else
{
/* Return the expanded name */
*ExpandedName = FullPathName;
FullPathName->Length = (USHORT)Length;
}
Quickie:
/* FIXME: Unlock the PEB */
//RtlLeaveCriticalSection(&FastPebLock);
/* Display debug output if snaps are on */
if (ShowSnaps)
{
/* Check which output to use -- failure or success */
if (NT_SUCCESS(Status))
{
DbgPrintEx(DPFLTR_LDR_ID,
DPFLTR_ERROR_LEVEL,
"LDR: %s - Expanded to %wZ\n",
__FUNCTION__,
*ExpandedName);
}
else
{
DbgPrintEx(DPFLTR_LDR_ID,
DPFLTR_ERROR_LEVEL,
"LDR: %s - Failed to expand %wZ; 0x%08x\n",
__FUNCTION__,
OriginalName,
Status);
}
}
/* If we failed, return NULL */
if (!NT_SUCCESS(Status)) *ExpandedName = NULL;
/* Return status */
return Status;
}
NTSTATUS
NTAPI
LdrpSearchPath(IN PWCHAR *SearchPath,
IN PWCHAR DllName,
IN PUNICODE_STRING PathName,
IN PUNICODE_STRING FullPathName,
IN PUNICODE_STRING *ExpandedName)
{
BOOLEAN TryAgain = FALSE;
PWCHAR ActualSearchPath = *SearchPath;
UNICODE_STRING TestName;
NTSTATUS Status;
PWCHAR Buffer, BufEnd = NULL;
ULONG Length = 0;
WCHAR p;
//PWCHAR pp;
/* Check if we don't have a search path */
if (!ActualSearchPath) *SearchPath = LdrpDefaultPath.Buffer;
/* Display debug output if snaps are on */
if (ShowSnaps)
{
DbgPrintEx(DPFLTR_LDR_ID,
DPFLTR_ERROR_LEVEL,
"LDR: %s - Looking for %ws in %ws\n",
__FUNCTION__,
DllName,
*SearchPath);
}
/* Check if we're dealing with a relative path */
if (RtlDetermineDosPathNameType_U(DllName) != RtlPathTypeRelative)
{
/* Good, we're not. Create the name string */
Status = RtlInitUnicodeStringEx(&TestName, DllName);
if (!NT_SUCCESS(Status)) goto Quickie;
/* Make sure it exists */
#if 0
if (!RtlDoesFileExists_UstrEx(&TestName, TRUE))
{
/* It doesn't, fail */
Status = STATUS_DLL_NOT_FOUND;
goto Quickie;
}
#endif
/* Resolve the full name */
Status = LdrpResolveFullName(&TestName,
PathName,
FullPathName,
ExpandedName);
goto Quickie;
}
/* FIXME: Handle relative case semicolon-lookup here */
/* Calculate length */
Length += (ULONG)wcslen(DllName) + 1;
if (Length > UNICODE_STRING_MAX_CHARS)
{
/* Too long, fail */
Status = STATUS_NAME_TOO_LONG;
goto Quickie;
}
/* Allocate buffer */
Buffer = RtlAllocateHeap(RtlGetProcessHeap(), 0, Length * sizeof(WCHAR));
if (!Buffer)
{
/* Fail */
Status = STATUS_NO_MEMORY;
goto Quickie;
}
/* FIXME: Setup TestName here */
Status = STATUS_NOT_FOUND;
BufEnd = Buffer;
/* Start loop */
do
{
/* Get character */
p = *ActualSearchPath;
if (!(p) || (p == ';'))
{
/* FIXME: We don't have a character, or is a semicolon.*/
/* Display debug output if snaps are on */
if (ShowSnaps)
{
DbgPrintEx(DPFLTR_LDR_ID,
DPFLTR_ERROR_LEVEL,
"LDR: %s - Looking for %ws\n",
__FUNCTION__,
Buffer);
}
/* Sanity check */
TestName.Length = (USHORT)ALIGN_DOWN((BufEnd - Buffer), WCHAR);
#if 0
ASSERT(TestName.Length < TestName.MaximumLength);
#endif
/* Check if the file exists */
#if 0
if (RtlDoesFileExists_UstrEx(&TestName, FALSE))
#endif
{
/* It does. Reallocate the buffer */
TestName.MaximumLength = (USHORT)ALIGN_DOWN((BufEnd - Buffer), WCHAR) + sizeof(WCHAR);
TestName.Buffer = RtlReAllocateHeap(RtlGetProcessHeap(),
0,
Buffer,
TestName.MaximumLength);
if (!TestName.Buffer)
{
/* Keep the old one */
TestName.Buffer = Buffer;
}
else
{
/* Update buffer */
Buffer = TestName.Buffer;
}
/* Make sure we have a buffer at least */
ASSERT(TestName.Buffer);
/* Resolve the name */
*SearchPath = ActualSearchPath++;
Status = LdrpResolveFullName(&TestName,
PathName,
FullPathName,
ExpandedName);
break;
}
/* Update buffer end */
BufEnd = Buffer;
/* Update string position */
//pp = ActualSearchPath++;
}
else
{
/* Otherwise, write the character */
*BufEnd = p;
BufEnd++;
}
/* Check if the string is empty, meaning we're done */
if (!(*ActualSearchPath)) TryAgain = TRUE;
/* Advance in the string */
ActualSearchPath++;
} while (!TryAgain);
/* Check if we had a buffer and free it */
if (Buffer) RtlFreeHeap(RtlGetProcessHeap(), 0, Buffer);
Quickie:
/* Check if we got here through failure */
if (!NT_SUCCESS(Status)) *ExpandedName = NULL;
/* Display debug output if snaps are on */
if (ShowSnaps)
{
/* Check which output to use -- failure or success */
if (NT_SUCCESS(Status))
{
DbgPrintEx(DPFLTR_LDR_ID,
DPFLTR_ERROR_LEVEL,
"LDR: %s - Returning %wZ\n",
__FUNCTION__,
*ExpandedName);
}
else
{
DbgPrintEx(DPFLTR_LDR_ID,
DPFLTR_ERROR_LEVEL,
"LDR: %s - Unable to locate %ws in %ws: 0x%08x\n",
__FUNCTION__,
DllName,
ActualSearchPath,
Status);
}
}
/* Return status */
return Status;
}
/* NOTE: This function is b0rked and in the process of being slowly unf*cked */
BOOLEAN
NTAPI
LdrpCheckForLoadedDll(IN PWSTR DllPath,
IN PUNICODE_STRING DllName,
IN BOOLEAN Flag,
IN BOOLEAN RedirectedDll,
OUT PLDR_DATA_TABLE_ENTRY *LdrEntry)
{
ULONG HashIndex;
PLIST_ENTRY ListHead, ListEntry;
PLDR_DATA_TABLE_ENTRY CurEntry;
BOOLEAN FullPath = FALSE;
PWCHAR wc;
WCHAR NameBuf[266];
UNICODE_STRING FullDllName, NtPathName;
ULONG Length;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS Status;
HANDLE FileHandle, SectionHandle;
IO_STATUS_BLOCK Iosb;
PVOID ViewBase = NULL;
SIZE_T ViewSize = 0;
PIMAGE_NT_HEADERS NtHeader, NtHeader2;
DPRINT("LdrpCheckForLoadedDll('%S' '%wZ' %u %u %p)\n", DllPath ? ((ULONG_PTR)DllPath == 1 ? L"" : DllPath) : L"", DllName, Flag, RedirectedDll, LdrEntry);
/* Check if a dll name was provided */
if (!(DllName->Buffer) || !(DllName->Buffer[0])) return FALSE;
/* FIXME: Warning, "Flag" is used as magic instead of "Static" */
/* FIXME: Warning, code does not support redirection at all */
/* Look in the hash table if flag was set */
lookinhash:
if (Flag /* the second check is a hack */ && !RedirectedDll)
{
/* FIXME: if we get redirected dll it means that we also get a full path so we need to find its filename for the hash lookup */
/* Get hash index */
HashIndex = LDR_GET_HASH_ENTRY(DllName->Buffer[0]);
/* Traverse that list */
ListHead = &LdrpHashTable[HashIndex];
ListEntry = ListHead->Flink;
while (ListEntry != ListHead)
{
/* Get the current entry */
CurEntry = CONTAINING_RECORD(ListEntry, LDR_DATA_TABLE_ENTRY, HashLinks);
/* Check base name of that module */
if (RtlEqualUnicodeString(DllName, &CurEntry->BaseDllName, TRUE))
{
/* It matches, return it */
*LdrEntry = CurEntry;
return TRUE;
}
/* Advance to the next entry */
ListEntry = ListEntry->Flink;
}
/* Module was not found, return failure */
return FALSE;
}
/* Check if this is a redirected DLL */
if (RedirectedDll)
{
/* Redirected dlls already have a full path */
FullPath = TRUE;
FullDllName = *DllName;
}
else
{
/* Check if there is a full path in this DLL */
wc = DllName->Buffer;
while (*wc)
{
/* Check for a slash in the current position*/
if ((*wc == L'\\') || (*wc == L'/'))
{
/* Found the slash, so dll name contains path */
FullPath = TRUE;
/* Setup full dll name string */
FullDllName.Buffer = NameBuf;
/* FIXME: This is from the Windows 2000 loader, not XP/2003, we should call LdrpSearchPath */
Length = RtlDosSearchPath_U(DllPath ? DllPath : LdrpDefaultPath.Buffer,
DllName->Buffer,
NULL,
sizeof(NameBuf) - sizeof(UNICODE_NULL),
FullDllName.Buffer,
NULL);
/* Check if that was successful */
if (!(Length) || (Length > (sizeof(NameBuf) - sizeof(UNICODE_NULL))))
{
if (ShowSnaps)
{
DPRINT1("LDR: LdrpCheckForLoadedDll - Unable To Locate %wZ: 0x%08x\n",
&DllName, Length);
}
}
/* Full dll name is found */
FullDllName.Length = Length;
FullDllName.MaximumLength = FullDllName.Length + sizeof(UNICODE_NULL);
break;
}
wc++;
}
}
/* Go check the hash table */
if (!FullPath)
{
Flag = TRUE;
goto lookinhash;
}
/* FIXME: Warning, activation context missing */
DPRINT("Warning, activation context missing\n");
/* NOTE: From here on down, everything looks good */
/* Loop the module list */
ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
ListEntry = ListHead->Flink;
while (ListEntry != ListHead)
{
/* Get the current entry and advance to the next one */
CurEntry = CONTAINING_RECORD(ListEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
ListEntry = ListEntry->Flink;
/* Check if it's being unloaded */
if (!CurEntry->InMemoryOrderLinks.Flink) continue;
/* Check if name matches */
if (RtlEqualUnicodeString(&FullDllName,
&CurEntry->FullDllName,
TRUE))
{
/* Found it */
*LdrEntry = CurEntry;
return TRUE;
}
}
/* Convert given path to NT path */
if (!RtlDosPathNameToNtPathName_U(FullDllName.Buffer,
&NtPathName,
NULL,
NULL))
{
/* Fail if conversion failed */
return FALSE;
}
/* Initialize object attributes and open it */
InitializeObjectAttributes(&ObjectAttributes,
&NtPathName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
Status = NtOpenFile(&FileHandle,
SYNCHRONIZE | FILE_EXECUTE,
&ObjectAttributes,
&Iosb,
FILE_SHARE_READ | FILE_SHARE_DELETE,
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT);
/* Free NT path name */
RtlFreeHeap(RtlGetProcessHeap(), 0, NtPathName.Buffer);
/* If opening the file failed - return failure */
if (!NT_SUCCESS(Status)) return FALSE;
/* Create a section for this file */
Status = NtCreateSection(&SectionHandle,
SECTION_MAP_READ |
SECTION_MAP_EXECUTE |
SECTION_MAP_WRITE,
NULL,
NULL,
PAGE_EXECUTE,
SEC_COMMIT,
FileHandle);
/* Close file handle */
NtClose(FileHandle);
/* If creating section failed - return failure */
if (!NT_SUCCESS(Status)) return FALSE;
/* Map view of this section */
Status = ZwMapViewOfSection(SectionHandle,
NtCurrentProcess(),
&ViewBase,
0,
0,
NULL,
&ViewSize,
ViewShare,
0,
PAGE_EXECUTE);
/* Close section handle */
NtClose(SectionHandle);
/* If section mapping failed - return failure */
if (!NT_SUCCESS(Status)) return FALSE;
/* Get pointer to the NT header of this section */
Status = RtlImageNtHeaderEx(0, ViewBase, ViewSize, &NtHeader);
if (!(NT_SUCCESS(Status)) || !(NtHeader))
{
/* Unmap the section and fail */
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
return FALSE;
}
/* Go through the list of modules again */
ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
ListEntry = ListHead->Flink;
while (ListEntry != ListHead)
{
/* Get the current entry and advance to the next one */
CurEntry = CONTAINING_RECORD(ListEntry,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks);
ListEntry = ListEntry->Flink;
/* Check if it's in the process of being unloaded */
if (!CurEntry->InMemoryOrderLinks.Flink) continue;
/* The header is untrusted, use SEH */
_SEH2_TRY
{
/* Check if timedate stamp and sizes match */
if ((CurEntry->TimeDateStamp == NtHeader->FileHeader.TimeDateStamp) &&
(CurEntry->SizeOfImage == NtHeader->OptionalHeader.SizeOfImage))
{
/* Time, date and size match. Let's compare their headers */
NtHeader2 = RtlImageNtHeader(CurEntry->DllBase);
if (RtlCompareMemory(NtHeader2, NtHeader, sizeof(IMAGE_NT_HEADERS)))
{
/* Headers match too! Finally ask the kernel to compare mapped files */
Status = ZwAreMappedFilesTheSame(CurEntry->DllBase, ViewBase);
if (NT_SUCCESS(Status))
{
/* This is our entry!, unmap and return success */
*LdrEntry = CurEntry;
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
_SEH2_YIELD(return TRUE;)
}
}
}
}
_SEH2_EXCEPT (EXCEPTION_EXECUTE_HANDLER)
{
_SEH2_YIELD(break;)
}
_SEH2_END;
}
/* Unmap the section and fail */
NtUnmapViewOfSection(NtCurrentProcess(), ViewBase);
return FALSE;
}
NTSTATUS
NTAPI
LdrpGetProcedureAddress(
_In_ PVOID BaseAddress,
_In_opt_ _When_(Ordinal == 0, _Notnull_) PANSI_STRING Name,
_In_opt_ _When_(Name == NULL, _In_range_(>, 0)) ULONG Ordinal,
_Out_ PVOID *ProcedureAddress,
_In_ BOOLEAN ExecuteInit)
{
NTSTATUS Status = STATUS_SUCCESS;
UCHAR ImportBuffer[64]; // 128 since NT6.2
PLDR_DATA_TABLE_ENTRY LdrEntry;
IMAGE_THUNK_DATA Thunk;
PVOID ImageBase;
PIMAGE_IMPORT_BY_NAME ImportName = NULL;
PIMAGE_EXPORT_DIRECTORY ExportDir;
ULONG ExportDirSize, Length;
PLIST_ENTRY Entry;
/* Show debug message */
if (ShowSnaps) DPRINT1("LDR: LdrGetProcedureAddress by ");
/* Check if we got a name */
if (Name)
{
/* Show debug message */
if (ShowSnaps) DbgPrint("NAME - %s\n", Name->Buffer);
/* Make sure it's not too long */
Length = Name->Length +
sizeof(CHAR) +
FIELD_OFFSET(IMAGE_IMPORT_BY_NAME, Name);
if (Length > UNICODE_STRING_MAX_BYTES)
{
/* Won't have enough space to add the hint */
return STATUS_NAME_TOO_LONG;
}
/* Check if our buffer is large enough */
if (Length > sizeof(ImportBuffer))
{
/* Allocate from heap, plus 2 bytes for the Hint */
ImportName = RtlAllocateHeap(RtlGetProcessHeap(),
0,
Length);
if (!ImportName)
{
/* Return STATUS_INSUFFICIENT_RESOURCES since NT6.2 */
return STATUS_INVALID_PARAMETER;
}
}
else
{
/* Use our internal buffer */
ImportName = (PIMAGE_IMPORT_BY_NAME)ImportBuffer;
}
/* Clear the hint */
ImportName->Hint = 0;
/* Copy the name and null-terminate it */
RtlCopyMemory(ImportName->Name, Name->Buffer, Name->Length);
ImportName->Name[Name->Length] = ANSI_NULL;
/* Clear the high bit */
ImageBase = ImportName;
Thunk.u1.AddressOfData = 0;
}
else
{
/* Do it by ordinal */
ImageBase = NULL;
/* Show debug message */
if (ShowSnaps) DbgPrint("ORDINAL - %lx\n", Ordinal);
/* Make sure an ordinal was given */
if (!Ordinal)
{
/* No ordinal */
DPRINT1("No ordinal and no name\n");
return STATUS_INVALID_PARAMETER;
}
/* Set the original flag in the thunk */
Thunk.u1.Ordinal = Ordinal | IMAGE_ORDINAL_FLAG;
}
/* Acquire lock unless we are initting */
if (!LdrpInLdrInit) RtlEnterCriticalSection(&LdrpLoaderLock);
_SEH2_TRY
{
/* Try to find the loaded DLL */
if (!LdrpCheckForLoadedDllHandle(BaseAddress, &LdrEntry))
{
/* Invalid base */
DPRINT1("Invalid base address %p\n", BaseAddress);
Status = STATUS_DLL_NOT_FOUND;
_SEH2_YIELD(goto Quickie;)
}
/* Get the pointer to the export directory */
ExportDir = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportDirSize);
if (!ExportDir)
{
DPRINT1("Image %wZ has no exports, but were trying to get procedure %Z. BaseAddress asked 0x%p, got entry BA 0x%p\n",
&LdrEntry->BaseDllName, Name, BaseAddress, LdrEntry->DllBase);
Status = STATUS_PROCEDURE_NOT_FOUND;
_SEH2_YIELD(goto Quickie;)
}
/* Now get the thunk */
Status = LdrpSnapThunk(LdrEntry->DllBase,
ImageBase,
&Thunk,
&Thunk,
ExportDir,
ExportDirSize,
FALSE,
NULL);
/* Finally, see if we're supposed to run the init routines */
if ((NT_SUCCESS(Status)) && (ExecuteInit))
{
/*
* It's possible a forwarded entry had us load the DLL. In that case,
* then we will call its DllMain. Use the last loaded DLL for this.
*/
Entry = NtCurrentPeb()->Ldr->InInitializationOrderModuleList.Blink;
LdrEntry = CONTAINING_RECORD(Entry,
LDR_DATA_TABLE_ENTRY,
InInitializationOrderLinks);
/* Make sure we didn't process it yet*/
if (!(LdrEntry->Flags & LDRP_ENTRY_PROCESSED))
{
/* Call the init routine */
_SEH2_TRY
{
Status = LdrpRunInitializeRoutines(NULL);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Get the exception code */
Status = _SEH2_GetExceptionCode();
}
_SEH2_END;
}
}
/* Make sure we're OK till here */
if (NT_SUCCESS(Status))
{
/* Return the address */
*ProcedureAddress = (PVOID)Thunk.u1.Function;
}
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* Just ignore exceptions */
}
_SEH2_END;
Quickie:
/* Cleanup */
if (ImportName && (ImportName != (PIMAGE_IMPORT_BY_NAME)ImportBuffer))
{
/* We allocated from heap, free it */
RtlFreeHeap(RtlGetProcessHeap(), 0, ImportName);
}
/* Release the CS if we entered it */
if (!LdrpInLdrInit) RtlLeaveCriticalSection(&LdrpLoaderLock);
/* We're done */
return Status;
}
NTSTATUS
NTAPI
LdrpLoadDll(IN BOOLEAN Redirected,
IN PWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN PUNICODE_STRING DllName,
OUT PVOID *BaseAddress,
IN BOOLEAN CallInit)
{
PPEB Peb = NtCurrentPeb();
NTSTATUS Status = STATUS_SUCCESS;
const WCHAR *p;
BOOLEAN GotExtension;
WCHAR c;
WCHAR NameBuffer[MAX_PATH + 6];
UNICODE_STRING RawDllName;
PLDR_DATA_TABLE_ENTRY LdrEntry;
BOOLEAN InInit = LdrpInLdrInit;
/* Save the Raw DLL Name */
if (DllName->Length >= sizeof(NameBuffer)) return STATUS_NAME_TOO_LONG;
RtlInitEmptyUnicodeString(&RawDllName, NameBuffer, sizeof(NameBuffer));
RtlCopyUnicodeString(&RawDllName, DllName);
/* Find the extension, if present */
/* NOTE: Access violation is expected here in some cases (Buffer[-1]) */
p = DllName->Buffer + DllName->Length / sizeof(WCHAR) - 1;
GotExtension = FALSE;
while (p >= DllName->Buffer)
{
c = *p--;
if (c == L'.')
{
GotExtension = TRUE;
break;
}
else if (c == L'\\')
{
break;
}
}
/* If no extension was found, add the default extension */
if (!GotExtension)
{
/* Check that we have space to add one */
if ((DllName->Length + LdrApiDefaultExtension.Length + sizeof(UNICODE_NULL)) >=
sizeof(NameBuffer))
{
/* No space to add the extension */
DbgPrintEx(DPFLTR_LDR_ID,
DPFLTR_ERROR_LEVEL,
"LDR: %s - Dll name missing extension; with extension "
"added the name is too long\n"
" DllName: (@ %p) \"%wZ\"\n"
" DllName->Length: %u\n",
__FUNCTION__,
DllName,
DllName,
DllName->Length);
return STATUS_NAME_TOO_LONG;
}
/* Add it. Needs to be null terminated, thus the length check above */
(VOID)RtlAppendUnicodeStringToString(&RawDllName,
&LdrApiDefaultExtension);
}
/* Check for init flag and acquire lock */
if (!InInit) RtlEnterCriticalSection(&LdrpLoaderLock);
_SEH2_TRY
{
/* Show debug message */
if (ShowSnaps)
{
DPRINT1("LDR: LdrLoadDll, loading %wZ from %ws\n",
&RawDllName,
DllPath ? DllPath : L"");
}
/* Check if the DLL is already loaded */
if (!LdrpCheckForLoadedDll(DllPath,
&RawDllName,
FALSE,
Redirected,
&LdrEntry))
{
/* Map it */
Status = LdrpMapDll(DllPath,
DllPath,
NameBuffer,
DllCharacteristics,
FALSE,
Redirected,
&LdrEntry);
if (!NT_SUCCESS(Status))
_SEH2_LEAVE;
/* FIXME: Need to mark the DLL range for the stack DB */
//RtlpStkMarkDllRange(LdrEntry);
/* Check if IMAGE_FILE_EXECUTABLE_IMAGE was provided */
if ((DllCharacteristics) &&
(*DllCharacteristics & IMAGE_FILE_EXECUTABLE_IMAGE))
{
/* This is not a DLL, so remove such data */
LdrEntry->EntryPoint = NULL;
LdrEntry->Flags &= ~LDRP_IMAGE_DLL;
}
/* Make sure it's a DLL */
if (LdrEntry->Flags & LDRP_IMAGE_DLL)
{
/* Check if this is a .NET Image */
if (!(LdrEntry->Flags & LDRP_COR_IMAGE))
{
/* Walk the Import Descriptor */
Status = LdrpWalkImportDescriptor(DllPath, LdrEntry);
}
/* Update load count, unless it's locked */
if (LdrEntry->LoadCount != 0xFFFF) LdrEntry->LoadCount++;
LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_REFCOUNT);
/* Check if we failed */
if (!NT_SUCCESS(Status))
{
/* Clear entrypoint, and insert into list */
LdrEntry->EntryPoint = NULL;
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
&LdrEntry->InInitializationOrderLinks);
/* Cancel the load */
LdrpClearLoadInProgress();
/* Unload the DLL */
if (ShowSnaps)
{
DbgPrint("LDR: Unloading %wZ due to error %x walking "
"import descriptors\n",
DllName,
Status);
}
LdrUnloadDll(LdrEntry->DllBase);
/* Return the error */
_SEH2_LEAVE;
}
}
else if (LdrEntry->LoadCount != 0xFFFF)
{
/* Increase load count */
LdrEntry->LoadCount++;
}
/* Insert it into the list */
InsertTailList(&Peb->Ldr->InInitializationOrderModuleList,
&LdrEntry->InInitializationOrderLinks);
/* If we have to run the entrypoint, make sure the DB is ready */
if (CallInit && LdrpLdrDatabaseIsSetup)
{
/* Notify Shim Engine */
if (g_ShimsEnabled)
{
VOID (NTAPI* SE_DllLoaded)(PLDR_DATA_TABLE_ENTRY) = RtlDecodeSystemPointer(g_pfnSE_DllLoaded);
SE_DllLoaded(LdrEntry);
}
/* Run the init routine */
Status = LdrpRunInitializeRoutines(NULL);
if (!NT_SUCCESS(Status))
{
/* Failed, unload the DLL */
if (ShowSnaps)
{
DbgPrint("LDR: Unloading %wZ because either its init "
"routine or one of its static imports failed; "
"status = 0x%08lx\n",
DllName,
Status);
}
LdrUnloadDll(LdrEntry->DllBase);
}
}
else
{
/* The DB isn't ready, which means we were loaded because of a forwarder */
Status = STATUS_SUCCESS;
}
}
else
{
/* We were already loaded. Are we a DLL? */
if ((LdrEntry->Flags & LDRP_IMAGE_DLL) && (LdrEntry->LoadCount != 0xFFFF))
{
/* Increase load count */
LdrEntry->LoadCount++;
LdrpUpdateLoadCount2(LdrEntry, LDRP_UPDATE_REFCOUNT);
/* Clear the load in progress */
LdrpClearLoadInProgress();
}
else
{
/* Not a DLL, just increase the load count */
if (LdrEntry->LoadCount != 0xFFFF) LdrEntry->LoadCount++;
}
}
}
_SEH2_FINALLY
{
/* Release the lock */
if (!InInit) RtlLeaveCriticalSection(&LdrpLoaderLock);
}
_SEH2_END;
/* Check for success */
if (NT_SUCCESS(Status))
{
/* Return the base address */
*BaseAddress = LdrEntry->DllBase;
}
else
{
/* Nothing found */
*BaseAddress = NULL;
}
/* Return status */
return Status;
}
ULONG
NTAPI
LdrpClearLoadInProgress(VOID)
{
PLIST_ENTRY ListHead, Entry;
PLDR_DATA_TABLE_ENTRY LdrEntry;
ULONG ModulesCount = 0;
/* Traverse the init list */
ListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList;
Entry = ListHead->Flink;
while (Entry != ListHead)
{
/* Get the loader entry */
LdrEntry = CONTAINING_RECORD(Entry,
LDR_DATA_TABLE_ENTRY,
InInitializationOrderLinks);
/* Clear load in progress flag */
LdrEntry->Flags &= ~LDRP_LOAD_IN_PROGRESS;
/* Check for modules with entry point count but not processed yet */
if ((LdrEntry->EntryPoint) &&
!(LdrEntry->Flags & LDRP_ENTRY_PROCESSED))
{
/* Increase counter */
ModulesCount++;
}
/* Advance to the next entry */
Entry = Entry->Flink;
}
/* Return final count */
return ModulesCount;
}
PVOID LdrpGetShimEngineFunction(PCSZ FunctionName)
{
ANSI_STRING Function;
NTSTATUS Status;
PVOID Address;
RtlInitAnsiString(&Function, FunctionName);
/* Skip Dll init */
Status = LdrpGetProcedureAddress(g_pShimEngineModule, &Function, 0, &Address, FALSE);
return NT_SUCCESS(Status) ? Address : NULL;
}
VOID
NTAPI
LdrpGetShimEngineInterface()
{
PVOID SE_DllLoaded = LdrpGetShimEngineFunction("SE_DllLoaded");
PVOID SE_DllUnloaded = LdrpGetShimEngineFunction("SE_DllUnloaded");
PVOID SE_InstallBeforeInit = LdrpGetShimEngineFunction("SE_InstallBeforeInit");
PVOID SE_InstallAfterInit = LdrpGetShimEngineFunction("SE_InstallAfterInit");
PVOID SE_ProcessDying = LdrpGetShimEngineFunction("SE_ProcessDying");
if (SE_DllLoaded && SE_DllUnloaded && SE_InstallBeforeInit && SE_InstallAfterInit && SE_ProcessDying)
{
g_pfnSE_DllLoaded = RtlEncodeSystemPointer(SE_DllLoaded);
g_pfnSE_DllUnloaded = RtlEncodeSystemPointer(SE_DllUnloaded);
g_pfnSE_InstallBeforeInit = RtlEncodeSystemPointer(SE_InstallBeforeInit);
g_pfnSE_InstallAfterInit = RtlEncodeSystemPointer(SE_InstallAfterInit);
g_pfnSE_ProcessDying = RtlEncodeSystemPointer(SE_ProcessDying);
g_ShimsEnabled = TRUE;
}
else
{
LdrpUnloadShimEngine();
}
}
VOID
NTAPI
LdrpRunShimEngineInitRoutine(IN ULONG Reason)
{
PLIST_ENTRY ListHead, Next;
PLDR_DATA_TABLE_ENTRY LdrEntry;
ListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList;
Next = ListHead->Flink;
while (Next != ListHead)
{
LdrEntry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
if (g_pShimEngineModule == LdrEntry->DllBase)
{
if (LdrEntry->EntryPoint)
{
_SEH2_TRY
{
LdrpCallInitRoutine(LdrEntry->EntryPoint, LdrEntry->DllBase, Reason, NULL);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
DPRINT1("WARNING: Exception 0x%x during LdrpRunShimEngineInitRoutine(%u)\n",
_SEH2_GetExceptionCode(), Reason);
}
_SEH2_END;
}
return;
}
Next = Next->Flink;
}
}
VOID
NTAPI
LdrpLoadShimEngine(IN PWSTR ImageName, IN PUNICODE_STRING ProcessImage, IN PVOID pShimData)
{
UNICODE_STRING ShimLibraryName;
PVOID ShimLibrary;
NTSTATUS Status;
RtlInitUnicodeString(&ShimLibraryName, ImageName);
/* We should NOT pass CallInit = TRUE!
If we do this, other init routines will be called before we get a chance to shim stuff.. */
Status = LdrpLoadDll(FALSE, NULL, NULL, &ShimLibraryName, &ShimLibrary, FALSE);
if (NT_SUCCESS(Status))
{
g_pShimEngineModule = ShimLibrary;
LdrpRunShimEngineInitRoutine(DLL_PROCESS_ATTACH);
LdrpGetShimEngineInterface();
if (g_ShimsEnabled)
{
VOID(NTAPI *SE_InstallBeforeInit)(PUNICODE_STRING, PVOID);
SE_InstallBeforeInit = RtlDecodeSystemPointer(g_pfnSE_InstallBeforeInit);
SE_InstallBeforeInit(ProcessImage, pShimData);
}
}
}
VOID
NTAPI
LdrpUnloadShimEngine()
{
/* Make sure we do not call into the shim engine anymore */
g_ShimsEnabled = FALSE;
LdrpRunShimEngineInitRoutine(DLL_PROCESS_DETACH);
LdrUnloadDll(g_pShimEngineModule);
g_pShimEngineModule = NULL;
}
/* EOF */