Windows2000/private/ntos/dll/ldrapi.c
2020-09-30 17:12:32 +02:00

962 lines
34 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
*/
#include "ldrp.h"
#define DLLEXTENSION ((PWSTR)".\0d\0l\0l\0\0") // .dll
ULONG LdrpClearLoadInProgress(VOID);
NTSTATUS LdrLoadDll(IN PWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN PUNICODE_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:
TBD
*/
{
return LdrpLoadDll(DllPath, DllCharacteristics, DllName, DllHandle, TRUE);
}
NTSTATUS NTAPI LdrpLoadDll(IN PWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN PUNICODE_STRING DllName,
OUT PVOID * DllHandle,
IN BOOLEAN RunInitRoutines)
{
PPEB Peb;
PTEB Teb;
NTSTATUS st;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PWSTR ActualDllName;
PWCH p, pp;
UNICODE_STRING ActualDllNameStr;
BOOLEAN Wx86KnownDll = FALSE;
WCHAR FreeBuffer[266];
Peb = NtCurrentPeb();
Teb = NtCurrentTeb();
st = STATUS_SUCCESS;
try {
// Grab Peb lock and Snap All Links to specified DLL
if (LdrpInLdrInit == FALSE) {
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
#if defined (WX86)
if (Teb->Wx86Thread.UseKnownWx86Dll) {
Wx86KnownDll = Teb->Wx86Thread.UseKnownWx86Dll;
Teb->Wx86Thread.UseKnownWx86Dll = FALSE;
}
#endif
p = DllName->Buffer;
pp = NULL;
while (*p) {
if (*p++ == (WCHAR)'.') {
pp = p;// pp will point to first character after last '.'
}
}
ActualDllName = FreeBuffer;
if (DllName->Length >= sizeof(FreeBuffer)) {
return STATUS_NAME_TOO_LONG;
}
RtlMoveMemory(ActualDllName, DllName->Buffer, DllName->Length);
if (!pp || *pp == (WCHAR)'\\') {// No extension found (just ..\)
if (DllName->Length + 10 >= sizeof(FreeBuffer)) {
return STATUS_NAME_TOO_LONG;
}
RtlMoveMemory((PCHAR)ActualDllName + DllName->Length, DLLEXTENSION, 10);
} else {
ActualDllName[DllName->Length >> 1] = UNICODE_NULL;
}
if (ShowSnaps) {
DbgPrint("LDR: LdrLoadDll, loading %ws from %ws\n", ActualDllName, ARGUMENT_PRESENT(DllPath) ? DllPath : L"");
}
RtlInitUnicodeString(&ActualDllNameStr, ActualDllName);
ActualDllNameStr.MaximumLength = sizeof(FreeBuffer);
if (!LdrpCheckForLoadedDll(DllPath, &ActualDllNameStr, FALSE, Wx86KnownDll, &LdrDataTableEntry)) {
st = LdrpMapDll(DllPath, ActualDllName, DllCharacteristics, FALSE, Wx86KnownDll, &LdrDataTableEntry);
if (!NT_SUCCESS(st)) {
return st;
}
if (ARGUMENT_PRESENT(DllCharacteristics) && *DllCharacteristics & IMAGE_FILE_EXECUTABLE_IMAGE) {
LdrDataTableEntry->EntryPoint = 0;
LdrDataTableEntry->Flags &= ~LDRP_IMAGE_DLL;
}
// and walk the import descriptor.
if (LdrDataTableEntry->Flags & LDRP_IMAGE_DLL) {
try {
st = LdrpWalkImportDescriptor(DllPath, LdrDataTableEntry);
} except(EXCEPTION_EXECUTE_HANDLER) {
st = GetExceptionCode();
}
if (LdrDataTableEntry->LoadCount != 0xffff) {
LdrDataTableEntry->LoadCount++;
}
LdrpReferenceLoadedDll(LdrDataTableEntry);
if (!NT_SUCCESS(st)) {
LdrDataTableEntry->EntryPoint = NULL;
InsertTailList(&NtCurrentPeb()->Ldr->InInitializationOrderModuleList, &LdrDataTableEntry->InInitializationOrderLinks);
LdrpClearLoadInProgress();
LdrUnloadDll((PVOID)LdrDataTableEntry->DllBase);
return st;
}
} else {
if (LdrDataTableEntry->LoadCount != 0xffff) {
LdrDataTableEntry->LoadCount++;
}
}
// Add init routine to list
InsertTailList(&NtCurrentPeb()->Ldr->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) {
try {
st = LdrpRunInitializeRoutines(NULL);
if (!NT_SUCCESS(st)) {
LdrUnloadDll((PVOID)LdrDataTableEntry->DllBase);
}
} except(EXCEPTION_EXECUTE_HANDLER) {
LdrUnloadDll((PVOID)LdrDataTableEntry->DllBase);
return GetExceptionCode();
}
} else {
st = STATUS_SUCCESS;
}
} else {// Count it. And everything that it imports.
if (LdrDataTableEntry->Flags & LDRP_IMAGE_DLL && LdrDataTableEntry->LoadCount != 0xffff) {
LdrDataTableEntry->LoadCount++;
LdrpReferenceLoadedDll(LdrDataTableEntry);
LdrpClearLoadInProgress();// Now clear the Load in progress bits
} else {
if (LdrDataTableEntry->LoadCount != 0xffff) {
LdrDataTableEntry->LoadCount++;
}
}
}
} finally {
if (LdrpInLdrInit == FALSE) {
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
}
if (NT_SUCCESS(st)) {
*DllHandle = (PVOID)LdrDataTableEntry->DllBase;
} else {
*DllHandle = NULL;
}
return st;
}
NTSTATUS LdrGetDllHandle(IN PWSTR DllPath OPTIONAL,
IN PULONG DllCharacteristics OPTIONAL,
IN PUNICODE_STRING DllName,
OUT PVOID * DllHandle
)
/*++
Routine Description:
This function locates the specified DLL and returns its handle.
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:
TBD
*/
{
NTSTATUS st;
PPEB Peb;
PTEB Teb;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PWSTR ActualDllName;
PWCH p, pp;
UNICODE_STRING ActualDllNameStr;
BOOLEAN Wx86KnownDll = FALSE;
WCHAR FreeBuffer[266];
DllCharacteristics;
Peb = NtCurrentPeb();
Teb = NtCurrentTeb();
st = STATUS_DLL_NOT_FOUND;
try {
// Grab Peb lock
if (LdrpInLdrInit == FALSE) {
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
#if defined (WX86)
if (Teb->Wx86Thread.UseKnownWx86Dll) {
Wx86KnownDll = Teb->Wx86Thread.UseKnownWx86Dll;
Teb->Wx86Thread.UseKnownWx86Dll = FALSE;
}
// No module handle cache lookup for Wx86,(relies on unique basename)
if (Wx86ProcessInit) {
LdrpGetModuleHandleCache = NULL;
}
#endif
if (LdrpGetModuleHandleCache) {
if (RtlEqualUnicodeString(DllName, &LdrpGetModuleHandleCache->BaseDllName, TRUE)) {
*DllHandle = (PVOID)LdrpGetModuleHandleCache->DllBase;
st = STATUS_SUCCESS;
leave;
}
}
p = DllName->Buffer;
pp = NULL;
while (*p) {
if (*p++ == (WCHAR)'.') {
// pp will point to first character after last '.'
pp = p;
}
}
ActualDllName = FreeBuffer;
if (DllName->Length >= sizeof(FreeBuffer)) {
return STATUS_NAME_TOO_LONG;
}
RtlMoveMemory(ActualDllName, DllName->Buffer, DllName->Length);
if (!pp || *pp == (WCHAR)'\\') {
// No extension found (just ..\)
if (DllName->Length + 10 >= sizeof(FreeBuffer)) {
return STATUS_NAME_TOO_LONG;
}
RtlMoveMemory((PCHAR)ActualDllName + DllName->Length, DLLEXTENSION, 10);
} else {// see if the name ends in . If it does, trim out the trailing
if (ActualDllName[(DllName->Length - 2) >> 1] == L'.') {
DllName->Length -= 2;
}
ActualDllName[DllName->Length >> 1] = UNICODE_NULL;
}
// Check the LdrTable to see if Dll has already been loaded into this image.
RtlInitUnicodeString(&ActualDllNameStr, ActualDllName);
ActualDllNameStr.MaximumLength = sizeof(FreeBuffer);
if (ShowSnaps) {
DbgPrint("LDR: LdrGetDllHandle, searching for %ws from %ws\n",
ActualDllName,
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), Wx86KnownDll, &LdrDataTableEntry)) {
*DllHandle = (PVOID)LdrDataTableEntry->DllBase;
LdrpGetModuleHandleCache = LdrDataTableEntry;
st = STATUS_SUCCESS;
}
} finally {
if (LdrpInLdrInit == FALSE) {
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
}
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:
TBD
*/
{
NTSTATUS st;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
st = STATUS_SUCCESS;
try {
if (LdrpInLdrInit == FALSE) {
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
if (LdrpShutdownInProgress) {
return STATUS_SUCCESS;
}
if (LdrpCheckForLoadedDllHandle(DllHandle, &LdrDataTableEntry)) {
if (LdrDataTableEntry->TlsIndex) {
st = STATUS_DLL_NOT_FOUND;
} else {
LdrDataTableEntry->Flags |= LDRP_DONT_CALL_FOR_THREADS;
}
}
} finally {
if (LdrpInLdrInit == FALSE) {
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
}
return st;
}
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:
TBD
*/
{
NTSTATUS st;
PPEB Peb;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PLDR_DATA_TABLE_ENTRY Entry;
PDLL_INIT_ROUTINE InitRoutine;
LIST_ENTRY LocalUnloadHead;
PLIST_ENTRY Next;
#if defined (WX86)
PVOID Wx86Plugin = NULL;
#endif
Peb = NtCurrentPeb();
st = STATUS_SUCCESS;
try {
// Grab Peb lock and decrement reference count of all affected DLLs
if (LdrpInLdrInit == FALSE) {
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
LdrpActiveUnloadCount++;
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--;
if (LdrDataTableEntry->Flags & LDRP_IMAGE_DLL) {
LdrpDereferenceLoadedDll(LdrDataTableEntry);
}
} 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 = Peb->Ldr->InInitializationOrderModuleList.Blink;
while (Next != &Peb->Ldr->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;
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) {
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));
// 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)LdrDataTableEntry->EntryPoint;
#if defined (WX86)
if (Wx86ProcessInit) {
try {
LdrpWx86DllProcessDetach(LdrDataTableEntry);
RemoveEntryList(&Entry->InLoadOrderLinks);
Entry = NULL;
Next = LdrpUnloadHead.Flink;
}
except(EXCEPTION_EXECUTE_HANDLER) {
goto top;
}
} else
#endif
if (InitRoutine && (LdrDataTableEntry->Flags & LDRP_PROCESS_ATTACH_CALLED)) {
try {
if (ShowSnaps) {
DbgPrint("LDR: Calling deinit %lx\n", InitRoutine);
}
LdrpCallInitRoutine(InitRoutine, LdrDataTableEntry->DllBase, DLL_PROCESS_DETACH, NULL);
RemoveEntryList(&Entry->InLoadOrderLinks);
Entry = NULL;
Next = LdrpUnloadHead.Flink;
} except(EXCEPTION_EXECUTE_HANDLER) {
goto top;
}
} else {
RemoveEntryList(&(Entry->InLoadOrderLinks));
Entry = NULL;
Next = LdrpUnloadHead.Flink;
}
}
bottom:
#if defined (WX86)
if (Wx86ProcessInit) {
// Notify Wx86 that the modules are about to be unmapped.
// This must be done in a separate pass from the unmap loop, as wx86.dll
// might be in the list of modules to unmap.
Next = LocalUnloadHead.Flink;
while (Next != &LocalUnloadHead) {
LdrDataTableEntry = (PLDR_DATA_TABLE_ENTRY)(CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, HashLinks));
Next = Next->Flink;
LdrpWx86DllMapNotify(LdrDataTableEntry->DllBase, FALSE);
// Remember if we are unloading a plugin dll
if ((LdrDataTableEntry->DllBase == DllHandle) && (LdrDataTableEntry->Flags & LDRP_WX86_PLUGIN)) {
Wx86Plugin = DllHandle;
}
}
}
#endif
// 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;
if (ShowSnaps) {
DbgPrint("LDR: Unmapping [%ws]\n", LdrDataTableEntry->BaseDllName.Buffer);
}
st = NtUnmapViewOfSection(NtCurrentProcess(), Entry->DllBase);
ASSERT(NT_SUCCESS(st));
LdrUnloadAlternateResourceModule(Entry->DllBase);
if (Entry->FullDllName.Buffer) {
RtlFreeUnicodeString(&Entry->FullDllName);
}
if (Entry->BaseDllName.Buffer) {
RtlFreeUnicodeString(&Entry->BaseDllName);
}
if (Entry == LdrpGetModuleHandleCache) {
LdrpGetModuleHandleCache = NULL;
}
RtlFreeHeap(Peb->ProcessHeap, 0, Entry);
}
leave_finally:;
} finally {
LdrpActiveUnloadCount--;
if (LdrpInLdrInit == FALSE) {
#if defined (WX86)
if (Wx86ProcessInit && Wx86Plugin) {
Wx86UnloadProviders(Wx86Plugin);
}
#endif
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
}
return st;
}
NTSTATUS LdrGetProcedureAddress(IN PVOID DllHandle,
IN PANSI_STRING ProcedureName OPTIONAL,
IN ULONG ProcedureNumber OPTIONAL,
OUT PVOID * ProcedureAddress)
{
return LdrpGetProcedureAddress(DllHandle, ProcedureName, ProcedureNumber, ProcedureAddress, TRUE);
}
NTSTATUS LdrpGetProcedureAddress(IN PVOID DllHandle,
IN PANSI_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:
TBD
*/
{
NTSTATUS st;
UCHAR FunctionNameBuffer[64];
PUCHAR src, dst;
ULONG cb, ExportSize;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
IMAGE_THUNK_DATA Thunk;
PVOID ImageBase;
PIMAGE_IMPORT_BY_NAME FunctionName;
PIMAGE_EXPORT_DIRECTORY ExportDirectory;
PLIST_ENTRY Next;
#if defined (WX86)
PTEB Teb;
BOOLEAN Wx86KnownDll = FALSE;
#endif
if (ShowSnaps) {
DbgPrint("LDR: LdrGetProcedureAddress by ");
}
#if defined (WX86)
Teb = NtCurrentTeb();
if (Teb->Wx86Thread.UseKnownWx86Dll) {
Wx86KnownDll = Teb->Wx86Thread.UseKnownWx86Dll;
Teb->Wx86Thread.UseKnownWx86Dll = FALSE;
}
#endif
FunctionName = NULL;
if (ARGUMENT_PRESENT(ProcedureName)) {
if (ShowSnaps) {
DbgPrint("NAME - %s\n", ProcedureName->Buffer);
}
// BUGBUG need STRING to PSZ
if (ProcedureName->Length >= sizeof(FunctionNameBuffer) - 1) {
FunctionName = RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG(TEMP_TAG), ProcedureName->Length + 1 + sizeof(USHORT));
if (!FunctionName) {
return STATUS_INVALID_PARAMETER;
}
} else {
FunctionName = (PIMAGE_IMPORT_BY_NAME)FunctionNameBuffer;
}
FunctionName->Hint = 0;
cb = ProcedureName->Length;
src = ProcedureName->Buffer;
dst = FunctionName->Name;
while (cb--) {
*dst++ = *src++;
}
*dst = '\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;
}
}
if (LdrpInLdrInit == FALSE) {
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
try {
if (!LdrpCheckForLoadedDllHandle(DllHandle, &LdrDataTableEntry)) {
st = STATUS_DLL_NOT_FOUND;
return st;
}
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)RtlImageDirectoryEntryToData(LdrDataTableEntry->DllBase,
TRUE,
IMAGE_DIRECTORY_ENTRY_EXPORT,
&ExportSize);
if (!ExportDirectory) {
return STATUS_PROCEDURE_NOT_FOUND;
}
st = LdrpSnapThunk(LdrDataTableEntry->DllBase, ImageBase, &Thunk, &Thunk, ExportDirectory, ExportSize, FALSE, NULL);
if (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 = NtCurrentPeb()->Ldr->InInitializationOrderModuleList.Blink;
LdrInitEntry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
if (!(LdrInitEntry->Flags & LDRP_ENTRY_PROCESSED)) {
try {
st = LdrpRunInitializeRoutines(NULL);
} except(EXCEPTION_EXECUTE_HANDLER) {
st = GetExceptionCode();
}
}
}
if (NT_SUCCESS(st)) {
*ProcedureAddress = (PVOID)Thunk.u1.Function;
#if defined (WX86)
// For dlls loaded as a Wx86 plugin ...
if (Wx86ProcessInit && (LdrDataTableEntry->Flags & LDRP_WX86_PLUGIN)) {
PVOID ExportThunk = NULL;
USHORT MachineType = RtlImageNtHeader(LdrDataTableEntry->DllBase)->FileHeader.Machine;
// and the GetProcAddress call is cross-architecture ...
if ((!Wx86KnownDll && (MachineType == IMAGE_FILE_MACHINE_I386)) || (Wx86KnownDll && (MachineType != IMAGE_FILE_MACHINE_I386))) {
// Thunk the export
st = Wx86ThunkPluginExport(LdrDataTableEntry->DllBase,
FunctionName ? FunctionName->Name : NULL,
ProcedureNumber,
(PVOID)(Thunk.u1.Function),
&ExportThunk);
if (NT_SUCCESS(st) && ExportThunk) {
*ProcedureAddress = ExportThunk;
} else {
// Don't let unthunked cross-architecture addresses get out
*ProcedureAddress = NULL;
st = STATUS_INVALID_IMAGE_FORMAT;
}
}
}
#endif
}
} finally {
if (FunctionName && (FunctionName != (PIMAGE_IMPORT_BY_NAME)FunctionNameBuffer)) {
RtlFreeHeap(RtlProcessHeap(), 0, FunctionName);
}
if (LdrpInLdrInit == FALSE) {
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
}
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;
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(ImportCallbackRoutine)) {
PIMAGE_NT_HEADERS NtHeaders;
PIMAGE_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 (ARGUMENT_PRESENT(ImageCharacteristics)) {
*ImageCharacteristics = NtHeaders->FileHeader.Characteristics;
}
ImportDescriptor = (PIMAGE_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(EXCEPTION_EXECUTE_HANDLER) {
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 LdrQueryProcessModuleInformation(OUT PRTL_PROCESS_MODULES ModuleInformation,
IN ULONG ModuleInformationLength,
OUT PULONG ReturnLength OPTIONAL)
{
NTSTATUS Status;
PPEB Peb;
PLIST_ENTRY LoadOrderListHead;
PLIST_ENTRY InitOrderListHead;
ULONG RequiredLength;
PLIST_ENTRY Next;
PLIST_ENTRY Next1;
PRTL_PROCESS_MODULE_INFORMATION ModuleInfo;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry1;
ANSI_STRING AnsiString;
PUCHAR s;
Peb = NtCurrentPeb();
// Grab Peb lock and capture information about of DLLs loaded in current process.
if (LdrpInLdrInit == FALSE) {
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
try {
RequiredLength = FIELD_OFFSET(RTL_PROCESS_MODULES, Modules);
if (ModuleInformationLength < RequiredLength) {
Status = STATUS_INFO_LENGTH_MISMATCH;
ModuleInfo = NULL;
} else {
ModuleInformation->NumberOfModules = 0;
ModuleInfo = &ModuleInformation->Modules[0];
Status = STATUS_SUCCESS;
}
LoadOrderListHead = &Peb->Ldr->InLoadOrderModuleList;
InitOrderListHead = &Peb->Ldr->InInitializationOrderModuleList;
Next = LoadOrderListHead->Flink;
while (Next != LoadOrderListHead) {
LdrDataTableEntry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
RequiredLength += sizeof(RTL_PROCESS_MODULE_INFORMATION);
if (ModuleInformationLength < RequiredLength) {
Status = STATUS_INFO_LENGTH_MISMATCH;
} else {
ModuleInfo->MappedBase = NULL;
ModuleInfo->ImageBase = LdrDataTableEntry->DllBase;
ModuleInfo->ImageSize = LdrDataTableEntry->SizeOfImage;
ModuleInfo->Flags = LdrDataTableEntry->Flags;
ModuleInfo->LoadCount = LdrDataTableEntry->LoadCount;
ModuleInfo->LoadOrderIndex = (USHORT)(ModuleInformation->NumberOfModules);
ModuleInfo->InitOrderIndex = 0;
Next1 = InitOrderListHead->Flink;
while (Next1 != InitOrderListHead) {
LdrDataTableEntry1 = CONTAINING_RECORD(Next1, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
ModuleInfo->InitOrderIndex++;
if (LdrDataTableEntry1 == LdrDataTableEntry) {
break;
}
Next1 = Next1->Flink;
}
AnsiString.Buffer = ModuleInfo->FullPathName;
AnsiString.Length = 0;
AnsiString.MaximumLength = sizeof(ModuleInfo->FullPathName);
RtlUnicodeStringToAnsiString(&AnsiString, &LdrDataTableEntry->FullDllName, FALSE);
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);
ModuleInfo++;
}
if (ModuleInformation != NULL) {
ModuleInformation->NumberOfModules++;
}
Next = Next->Flink;
}
if (ARGUMENT_PRESENT(ReturnLength)) {
*ReturnLength = RequiredLength;
}
} finally {
if (LdrpInLdrInit == FALSE) {
RtlLeaveCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
}
}
return(Status);
}
#if defined(MIPS)
VOID LdrProcessStarterHelper(IN PPROCESS_STARTER_ROUTINE ProcessStarter, IN PVOID RealStartAddress)
/*++
Routine Description:
This function is used to call the code that calls the initial entry point of a win32 process.
On all other platforms, this wrapper is not used since they can cope with a process entrypoint being in kernel32 prior to it being mapped.
Arguments:
ProcessStarter - Supplies the address of the function in Kernel32 that ends up calling the real process entrypoint
RealStartAddress - Supplies the real start address of the process.
*/
{
(ProcessStarter)(RealStartAddress);
NtTerminateProcess(NtCurrentProcess(), 0);
}
#endif