mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1277 lines
36 KiB
1277 lines
36 KiB
/*++
|
|
|
|
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"
|
|
|
|
#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;
|
|
NTSTATUS st;
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PWSTR ActualDllName;
|
|
PWCH p, pp;
|
|
UNICODE_STRING ActualDllNameStr;
|
|
#if defined (WX86)
|
|
PWX86TIB Wx86Tib;
|
|
#endif
|
|
BOOLEAN Wx86KnownDll = FALSE;
|
|
WCHAR FreeBuffer[266];
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
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)
|
|
Wx86Tib = Wx86CurrentTib();
|
|
if (Wx86Tib) {
|
|
if (Wx86Tib->UseKnownWx86Dll) {
|
|
Wx86KnownDll = Wx86Tib->UseKnownWx86Dll;
|
|
Wx86Tib->UseKnownWx86Dll = FALSE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
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 {
|
|
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);
|
|
|
|
//
|
|
// Now clear the Load in progress bits
|
|
//
|
|
|
|
LdrpClearLoadInProgress();
|
|
|
|
}
|
|
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;
|
|
PLDR_DATA_TABLE_ENTRY LdrDataTableEntry;
|
|
PWSTR ActualDllName;
|
|
PWCH p, pp;
|
|
UNICODE_STRING ActualDllNameStr;
|
|
#if defined (WX86)
|
|
PWX86TIB Wx86Tib;
|
|
#endif
|
|
BOOLEAN Wx86KnownDll = FALSE;
|
|
WCHAR FreeBuffer[266];
|
|
|
|
|
|
DllCharacteristics;
|
|
|
|
Peb = NtCurrentPeb();
|
|
|
|
st = STATUS_DLL_NOT_FOUND;
|
|
|
|
try {
|
|
|
|
//
|
|
// Grab Peb lock
|
|
//
|
|
|
|
if ( LdrpInLdrInit == FALSE ) {
|
|
RtlEnterCriticalSection((PRTL_CRITICAL_SECTION)NtCurrentPeb()->LoaderLock);
|
|
}
|
|
|
|
#if defined (WX86)
|
|
Wx86Tib = Wx86CurrentTib();
|
|
if (Wx86Tib) {
|
|
if (Wx86Tib->UseKnownWx86Dll) {
|
|
Wx86KnownDll = Wx86Tib->UseKnownWx86Dll;
|
|
Wx86Tib->UseKnownWx86Dll = FALSE;
|
|
}
|
|
|
|
//
|
|
// No module handle cache lookup for Wx86,
|
|
// (relies on unique basename)
|
|
//
|
|
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;
|
|
|
|
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 (InitRoutine && (LdrDataTableEntry->Flags & LDRP_PROCESS_ATTACH_CALLED) ) {
|
|
try {
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Calling deinit %lx\n",InitRoutine);
|
|
}
|
|
|
|
#if defined (WX86)
|
|
if (!Wx86CurrentTib() ||
|
|
LdrpRunWx86DllEntryPoint(InitRoutine,
|
|
NULL,
|
|
LdrDataTableEntry->DllBase,
|
|
DLL_PROCESS_DETACH,
|
|
NULL
|
|
) == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)
|
|
#endif
|
|
{
|
|
(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 (Wx86CurrentTib() && Wx86DllMapNotify) {
|
|
//
|
|
// 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;
|
|
|
|
(*Wx86DllMapNotify)(LdrDataTableEntry->DllBase, FALSE, NULL);
|
|
}
|
|
}
|
|
#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 DBG
|
|
RtlLogEvent( LdrpUnloadModuleEventId,
|
|
0,
|
|
&LdrDataTableEntry->FullDllName,
|
|
LdrDataTableEntry->DllBase
|
|
);
|
|
#endif // DBG
|
|
if (ShowSnaps) {
|
|
DbgPrint("LDR: Unmapping [%ws]\n",
|
|
LdrDataTableEntry->BaseDllName.Buffer
|
|
);
|
|
}
|
|
st = NtUnmapViewOfSection(NtCurrentProcess(),Entry->DllBase);
|
|
ASSERT(NT_SUCCESS(st));
|
|
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 ) {
|
|
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 (ShowSnaps) {
|
|
DbgPrint("LDR: LdrGetProcedureAddress by ");
|
|
}
|
|
|
|
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;
|
|
|
|
ImageBase = NtCurrentPeb()->ImageBaseAddress;
|
|
while (cb--) {
|
|
*dst++ = *src++;
|
|
}
|
|
*dst = '\0';
|
|
|
|
Thunk.u1.AddressOfData = FunctionName;
|
|
|
|
} else {
|
|
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,
|
|
0,
|
|
&Thunk,
|
|
&Thunk,
|
|
ExportDirectory,
|
|
ExportSize,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if ( RunInitRoutines ) {
|
|
|
|
//
|
|
// 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;
|
|
LdrDataTableEntry = CONTAINING_RECORD(Next, LDR_DATA_TABLE_ENTRY, InInitializationOrderLinks);
|
|
if ( !(LdrDataTableEntry->Flags & LDRP_ENTRY_PROCESSED) ) {
|
|
try {
|
|
st = LdrpRunInitializeRoutines(NULL);
|
|
}
|
|
except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
st = GetExceptionCode();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ( NT_SUCCESS(st) ) {
|
|
*ProcedureAddress = Thunk.u1.Function;
|
|
}
|
|
} 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;
|
|
ULONG ViewSize;
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
FILE_STANDARD_INFORMATION StandardInfo;
|
|
PIMAGE_SECTION_HEADER LastRvaSection;
|
|
BOOLEAN b;
|
|
|
|
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 {
|
|
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
|
|
.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
(ProcessStarter)(RealStartAddress);
|
|
NtTerminateProcess(NtCurrentProcess(),0);
|
|
}
|
|
|
|
#endif
|