X-Git-Url: https://git.reactos.org/?p=reactos.git;a=blobdiff_plain;f=reactos%2Flib%2Fntdll%2Fldr%2Futils.c;h=1c9240d25646787789aa6dc74fc0c63b47d98193;hp=2abe85f33dc08ba43a0f50ee578e655ec5ac8658;hb=ada897443b98f24f31d0671db5a79c6a62c14335;hpb=8dacbd3ab0d1ccef69d0b5fc3a22cdf4523ffd67 diff --git a/reactos/lib/ntdll/ldr/utils.c b/reactos/lib/ntdll/ldr/utils.c index 2abe85f33dc..1c9240d2564 100644 --- a/reactos/lib/ntdll/ldr/utils.c +++ b/reactos/lib/ntdll/ldr/utils.c @@ -1,105 +1,414 @@ -/* $Id: utils.c,v 1.45 2001/07/02 20:27:41 phreak Exp $ - * +/* $Id$ + * * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel - * FILE: lib/ntdll/ldr/startup.c + * FILE: lib/ntdll/ldr/utils.c * PURPOSE: Process startup for PE executables * PROGRAMMERS: Jean Michault * Rex Jolliff (rex@lvcablemodem.com) + * Hartmut Birr */ /* * TODO: - * - Fix calling of entry points - * - Handle loading flags correctly - * - any more ?? + * - Handle loading flags correctly + * - Handle errors correctly (unload dll's) + * - Implement a faster way to find modules (hash table) + * - any more ?? */ /* INCLUDES *****************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include +#include +#define NDEBUG +#include +#define LDRP_PROCESS_CREATION_TIME 0x8000000 -#ifdef DBG_NTDLL_LDR_UTILS -#define NDEBUG -#endif -#include +/* GLOBALS *******************************************************************/ -/* PROTOTYPES ****************************************************************/ +#ifdef NDEBUG +#if defined(__GNUC__) +#define TRACE_LDR(args...) if (RtlGetNtGlobalFlags() & FLG_SHOW_LDR_SNAPS) { DbgPrint("(LDR:%s:%d) ",__FILE__,__LINE__); DbgPrint(args); } +#else +#endif /* __GNUC__ */ +#else +#define TRACE_LDR(args...) do { DbgPrint("(LDR:%s:%d) ",__FILE__,__LINE__); DbgPrint(args); } while(0) +#endif +typedef struct _TLS_DATA +{ + PVOID StartAddressOfRawData; + DWORD TlsDataSize; + DWORD TlsZeroSize; + PIMAGE_TLS_CALLBACK TlsAddressOfCallBacks; + PLDR_DATA_TABLE_ENTRY Module; +} TLS_DATA, *PTLS_DATA; + +static PTLS_DATA LdrpTlsArray = NULL; +static ULONG LdrpTlsCount = 0; +static ULONG LdrpTlsSize = 0; +static HANDLE LdrpKnownDllsDirHandle = NULL; +static UNICODE_STRING LdrpKnownDllPath = {0, 0, NULL}; +static PLDR_DATA_TABLE_ENTRY LdrpLastModule = NULL; +extern PLDR_DATA_TABLE_ENTRY ExeModule; -/* Type for a DLL's entry point */ -typedef -WINBOOL -STDCALL -(* PDLLMAIN_FUNC) ( - HANDLE hInst, - ULONG ul_reason_for_call, - LPVOID lpReserved - ); +/* PROTOTYPES ****************************************************************/ -static NTSTATUS LdrFindDll(PLDR_MODULE *Dll,PUNICODE_STRING Name); +static NTSTATUS LdrFindEntryForName(PUNICODE_STRING Name, PLDR_DATA_TABLE_ENTRY *Module, BOOLEAN Ref); static PVOID LdrFixupForward(PCHAR ForwardName); static PVOID LdrGetExportByName(PVOID BaseAddress, PUCHAR SymbolName, USHORT Hint); - +static NTSTATUS LdrpLoadModule(IN PWSTR SearchPath OPTIONAL, + IN ULONG LoadFlags, + IN PUNICODE_STRING Name, + OUT PLDR_DATA_TABLE_ENTRY *Module, + OUT PVOID *BaseAddress OPTIONAL); +static NTSTATUS LdrpAttachProcess(VOID); +static VOID LdrpDetachProcess(BOOLEAN UnloadAll); /* FUNCTIONS *****************************************************************/ +#if defined(DBG) || defined(KDBG) -#ifdef KDBG - -VOID LdrLoadModuleSymbols(PLDR_MODULE ModuleObject) +VOID +LdrpLoadUserModuleSymbols(PLDR_DATA_TABLE_ENTRY LdrModule) { NtSystemDebugControl( - 0xffffffff, - (PVOID)ModuleObject, + DebugDbgLoadSymbols, + (PVOID)LdrModule, 0, NULL, 0, NULL); } -#endif /* KDBG */ +#endif /* DBG || KDBG */ + +BOOLEAN +LdrMappedAsDataFile(PVOID *BaseAddress) +{ + if (0 != ((DWORD_PTR) *BaseAddress & (PAGE_SIZE - 1))) + { + *BaseAddress = (PVOID) ((DWORD_PTR) *BaseAddress & ~ ((DWORD_PTR) PAGE_SIZE - 1)); + return TRUE; + } + + return FALSE; +} + +static __inline LONG LdrpDecrementLoadCount(PLDR_DATA_TABLE_ENTRY Module, BOOLEAN Locked) +{ + LONG LoadCount; + if (!Locked) + { + RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); + } + LoadCount = Module->LoadCount; + if (Module->LoadCount > 0 && Module->LoadCount != 0xFFFF) + { + Module->LoadCount--; + } + if (!Locked) + { + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + } + return LoadCount; +} + +static __inline LONG LdrpIncrementLoadCount(PLDR_DATA_TABLE_ENTRY Module, BOOLEAN Locked) +{ + LONG LoadCount; + if (!Locked) + { + RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); + } + LoadCount = Module->LoadCount; + if (Module->LoadCount != 0xFFFF) + { + Module->LoadCount++; + } + if (!Locked) + { + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + } + return LoadCount; +} + +static __inline VOID LdrpAcquireTlsSlot(PLDR_DATA_TABLE_ENTRY Module, ULONG Size, BOOLEAN Locked) +{ + if (!Locked) + { + RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); + } + Module->TlsIndex = (SHORT)LdrpTlsCount; + LdrpTlsCount++; + LdrpTlsSize += Size; + if (!Locked) + { + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + } +} + +static __inline VOID LdrpTlsCallback(PLDR_DATA_TABLE_ENTRY Module, ULONG dwReason) +{ + PIMAGE_TLS_CALLBACK TlsCallback; + if (Module->TlsIndex != 0xFFFF && Module->LoadCount == 0xFFFF) + { + TlsCallback = LdrpTlsArray[Module->TlsIndex].TlsAddressOfCallBacks; + if (TlsCallback) + { + while (*TlsCallback) + { + TRACE_LDR("%wZ - Calling tls callback at %x\n", + &Module->BaseDllName, TlsCallback); + TlsCallback(Module->DllBase, dwReason, NULL); + TlsCallback = (PIMAGE_TLS_CALLBACK)((ULONG_PTR)TlsCallback + sizeof(PVOID)); + } + } + } +} + +static BOOLEAN LdrpCallDllEntry(PLDR_DATA_TABLE_ENTRY Module, DWORD dwReason, PVOID lpReserved) +{ + if (!(Module->Flags & LDRP_IMAGE_DLL) || + Module->EntryPoint == 0) + { + return TRUE; + } + LdrpTlsCallback(Module, dwReason); + return ((PDLLMAIN_FUNC)Module->EntryPoint)(Module->DllBase, dwReason, lpReserved); +} + +static NTSTATUS +LdrpInitializeTlsForThread(VOID) +{ + PVOID* TlsPointers; + PTLS_DATA TlsInfo; + PVOID TlsData; + ULONG i; + PTEB Teb = NtCurrentTeb(); + + DPRINT("LdrpInitializeTlsForThread() called for %wZ\n", &ExeModule->BaseDllName); + + Teb->StaticUnicodeString.Length = 0; + Teb->StaticUnicodeString.MaximumLength = sizeof(Teb->StaticUnicodeBuffer); + Teb->StaticUnicodeString.Buffer = Teb->StaticUnicodeBuffer; + + if (LdrpTlsCount > 0) + { + TlsPointers = RtlAllocateHeap(RtlGetProcessHeap(), + 0, + LdrpTlsCount * sizeof(PVOID) + LdrpTlsSize); + if (TlsPointers == NULL) + { + DPRINT1("failed to allocate thread tls data\n"); + return STATUS_NO_MEMORY; + } + + TlsData = (PVOID)((ULONG_PTR)TlsPointers + LdrpTlsCount * sizeof(PVOID)); + Teb->ThreadLocalStoragePointer = TlsPointers; + + TlsInfo = LdrpTlsArray; + for (i = 0; i < LdrpTlsCount; i++, TlsInfo++) + { + TRACE_LDR("Initialize tls data for %wZ\n", &TlsInfo->Module->BaseDllName); + TlsPointers[i] = TlsData; + if (TlsInfo->TlsDataSize) + { + memcpy(TlsData, TlsInfo->StartAddressOfRawData, TlsInfo->TlsDataSize); + TlsData = (PVOID)((ULONG_PTR)TlsData + TlsInfo->TlsDataSize); + } + if (TlsInfo->TlsZeroSize) + { + memset(TlsData, 0, TlsInfo->TlsZeroSize); + TlsData = (PVOID)((ULONG_PTR)TlsData + TlsInfo->TlsZeroSize); + } + } + } + DPRINT("LdrpInitializeTlsForThread() done\n"); + return STATUS_SUCCESS; +} + +static NTSTATUS +LdrpInitializeTlsForProccess(VOID) +{ + PLIST_ENTRY ModuleListHead; + PLIST_ENTRY Entry; + PLDR_DATA_TABLE_ENTRY Module; + PIMAGE_TLS_DIRECTORY TlsDirectory; + PTLS_DATA TlsData; + ULONG Size; + + DPRINT("LdrpInitializeTlsForProccess() called for %wZ\n", &ExeModule->BaseDllName); + + if (LdrpTlsCount > 0) + { + LdrpTlsArray = RtlAllocateHeap(RtlGetProcessHeap(), + 0, + LdrpTlsCount * sizeof(TLS_DATA)); + if (LdrpTlsArray == NULL) + { + DPRINT1("Failed to allocate global tls data\n"); + return STATUS_NO_MEMORY; + } + + ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; + Entry = ModuleListHead->Flink; + while (Entry != ModuleListHead) + { + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList); + if (Module->LoadCount == 0xFFFF && + Module->TlsIndex != 0xFFFF) + { + TlsDirectory = (PIMAGE_TLS_DIRECTORY) + RtlImageDirectoryEntryToData(Module->DllBase, + TRUE, + IMAGE_DIRECTORY_ENTRY_TLS, + &Size); + ASSERT(Module->TlsIndex < LdrpTlsCount); + TlsData = &LdrpTlsArray[Module->TlsIndex]; + TlsData->StartAddressOfRawData = (PVOID)TlsDirectory->StartAddressOfRawData; + TlsData->TlsDataSize = TlsDirectory->EndAddressOfRawData - TlsDirectory->StartAddressOfRawData; + TlsData->TlsZeroSize = TlsDirectory->SizeOfZeroFill; + if (TlsDirectory->AddressOfCallBacks) + TlsData->TlsAddressOfCallBacks = *(PIMAGE_TLS_CALLBACK*)TlsDirectory->AddressOfCallBacks; + else + TlsData->TlsAddressOfCallBacks = NULL; + TlsData->Module = Module; +#if 0 + DbgPrint("TLS directory for %wZ\n", &Module->BaseDllName); + DbgPrint("StartAddressOfRawData: %x\n", TlsDirectory->StartAddressOfRawData); + DbgPrint("EndAddressOfRawData: %x\n", TlsDirectory->EndAddressOfRawData); + DbgPrint("SizeOfRawData: %d\n", TlsDirectory->EndAddressOfRawData - TlsDirectory->StartAddressOfRawData); + DbgPrint("AddressOfIndex: %x\n", TlsDirectory->AddressOfIndex); + DbgPrint("AddressOfCallBacks: %x (%x)\n", TlsDirectory->AddressOfCallBacks, *TlsDirectory->AddressOfCallBacks); + DbgPrint("SizeOfZeroFill: %d\n", TlsDirectory->SizeOfZeroFill); + DbgPrint("Characteristics: %x\n", TlsDirectory->Characteristics); +#endif + /* + * FIXME: + * Is this region allways writable ? + */ + *(PULONG)TlsDirectory->AddressOfIndex = Module->TlsIndex; + } + Entry = Entry->Flink; + } + } + DPRINT("LdrpInitializeTlsForProccess() done\n"); + return STATUS_SUCCESS; +} + +VOID +LdrpInitLoader(VOID) +{ + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING LinkTarget; + UNICODE_STRING Name; + HANDLE LinkHandle; + ULONG Length; + NTSTATUS Status; + + DPRINT("LdrpInitLoader() called for %wZ\n", &ExeModule->BaseDllName); + + /* Get handle to the 'KnownDlls' directory */ + RtlInitUnicodeString(&Name, + L"\\KnownDlls"); + InitializeObjectAttributes(&ObjectAttributes, + &Name, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + Status = NtOpenDirectoryObject(&LdrpKnownDllsDirHandle, + DIRECTORY_QUERY | DIRECTORY_TRAVERSE, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT("NtOpenDirectoryObject() failed (Status %lx)\n", Status); + LdrpKnownDllsDirHandle = NULL; + return; + } + + /* Allocate target name string */ + LinkTarget.Length = 0; + LinkTarget.MaximumLength = MAX_PATH * sizeof(WCHAR); + LinkTarget.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), + 0, + MAX_PATH * sizeof(WCHAR)); + if (LinkTarget.Buffer == NULL) + { + NtClose(LdrpKnownDllsDirHandle); + LdrpKnownDllsDirHandle = NULL; + return; + } + + RtlInitUnicodeString(&Name, + L"KnownDllPath"); + InitializeObjectAttributes(&ObjectAttributes, + &Name, + OBJ_CASE_INSENSITIVE | OBJ_OPENLINK, + LdrpKnownDllsDirHandle, + NULL); + Status = NtOpenSymbolicLinkObject(&LinkHandle, + SYMBOLIC_LINK_ALL_ACCESS, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + RtlFreeUnicodeString(&LinkTarget); + NtClose(LdrpKnownDllsDirHandle); + LdrpKnownDllsDirHandle = NULL; + return; + } + + Status = NtQuerySymbolicLinkObject(LinkHandle, + &LinkTarget, + &Length); + NtClose(LinkHandle); + if (!NT_SUCCESS(Status)) + { + RtlFreeUnicodeString(&LinkTarget); + NtClose(LdrpKnownDllsDirHandle); + LdrpKnownDllsDirHandle = NULL; + } + + RtlCreateUnicodeString(&LdrpKnownDllPath, + LinkTarget.Buffer); + + RtlFreeUnicodeString(&LinkTarget); + + DPRINT("LdrpInitLoader() done\n"); +} /*************************************************************************** - * NAME LOCAL - * LdrAdjustDllName + * NAME LOCAL + * LdrAdjustDllName * * DESCRIPTION - * Adjusts the name of a dll to a fully qualified name. + * Adjusts the name of a dll to a fully qualified name. * * ARGUMENTS - * FullDllName: Pointer to caller supplied storage for the fully - * qualified dll name. - * DllName: Pointer to the dll name. - * BaseName: TRUE: Only the file name is passed to FullDllName - * FALSE: The full path is preserved in FullDllName + * FullDllName: Pointer to caller supplied storage for the fully + * qualified dll name. + * DllName: Pointer to the dll name. + * BaseName: TRUE: Only the file name is passed to FullDllName + * FALSE: The full path is preserved in FullDllName * * RETURN VALUE - * None + * None * * REVISIONS * * NOTE - * A given path is not affected by the adjustment, but the file - * name only: - * ntdll --> ntdll.dll - * ntdll. --> ntdll - * ntdll.xyz --> ntdll.xyz + * A given path is not affected by the adjustment, but the file + * name only: + * ntdll --> ntdll.dll + * ntdll. --> ntdll + * ntdll.xyz --> ntdll.xyz */ - static VOID LdrAdjustDllName (PUNICODE_STRING FullDllName, - PUNICODE_STRING DllName, - BOOLEAN BaseName) + PUNICODE_STRING DllName, + BOOLEAN BaseName) { WCHAR Buffer[MAX_PATH]; ULONG Length; @@ -108,190 +417,252 @@ LdrAdjustDllName (PUNICODE_STRING FullDllName, Length = DllName->Length / sizeof(WCHAR); - if (BaseName == TRUE) + if (BaseName) { - /* get the base dll name */ - Pointer = DllName->Buffer + Length; - Extension = Pointer; - - do - { - --Pointer; - } - while (Pointer >= DllName->Buffer && *Pointer != L'\\' && *Pointer != L'/'); - - Pointer++; - Length = Extension - Pointer; - memmove (Buffer, Pointer, Length * sizeof(WCHAR)); + /* get the base dll name */ + Pointer = DllName->Buffer + Length; + Extension = Pointer; + + do + { + --Pointer; + } + while (Pointer >= DllName->Buffer && *Pointer != L'\\' && *Pointer != L'/'); + + Pointer++; + Length = Extension - Pointer; + memmove (Buffer, Pointer, Length * sizeof(WCHAR)); + Buffer[Length] = L'\0'; } else { - /* get the full dll name */ - memmove (Buffer, DllName->Buffer, DllName->Length); + /* get the full dll name */ + memmove (Buffer, DllName->Buffer, DllName->Length); + Buffer[DllName->Length / sizeof(WCHAR)] = L'\0'; } /* Build the DLL's absolute name */ Extension = wcsrchr (Buffer, L'.'); if ((Extension != NULL) && (*Extension == L'.')) { - /* with extension - remove dot if it's the last character */ - if (Buffer[Length - 1] == L'.') - Length--; - Buffer[Length] = 0; + /* with extension - remove dot if it's the last character */ + if (Buffer[Length - 1] == L'.') + Length--; + Buffer[Length] = 0; } else { - /* name without extension - assume that it is .dll */ - memmove (Buffer + Length, L".dll", 10); + /* name without extension - assume that it is .dll */ + memmove (Buffer + Length, L".dll", 10); } - RtlCreateUnicodeString (FullDllName, - Buffer); + RtlCreateUnicodeString(FullDllName, Buffer); } +PLDR_DATA_TABLE_ENTRY +LdrAddModuleEntry(PVOID ImageBase, + PIMAGE_NT_HEADERS NTHeaders, + PWSTR FullDosName) +{ + PLDR_DATA_TABLE_ENTRY Module; -/*************************************************************************** - * NAME EXPORTED - * LdrLoadDll - * - * DESCRIPTION - * - * ARGUMENTS - * - * RETURN VALUE - * - * REVISIONS - * - * NOTE - * - */ + Module = RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof (LDR_DATA_TABLE_ENTRY)); + ASSERT(Module); + memset(Module, 0, sizeof(LDR_DATA_TABLE_ENTRY)); + Module->DllBase = (PVOID)ImageBase; + Module->EntryPoint = (PVOID)NTHeaders->OptionalHeader.AddressOfEntryPoint; + if (Module->EntryPoint != 0) + Module->EntryPoint = (PVOID)((ULONG_PTR)Module->EntryPoint + (ULONG_PTR)Module->DllBase); + Module->SizeOfImage = LdrpGetResidentSize(NTHeaders); + if (NtCurrentPeb()->Ldr->Initialized == TRUE) + { + /* loading while app is running */ + Module->LoadCount = 1; + } else { + /* + * loading while app is initializing + * dll must not be unloaded + */ + Module->LoadCount = 0xFFFF; + } -NTSTATUS STDCALL -LdrLoadDll (IN PWSTR SearchPath OPTIONAL, - IN ULONG LoadFlags, - IN PUNICODE_STRING Name, - OUT PVOID *BaseAddress OPTIONAL) + Module->Flags = 0; + Module->TlsIndex = -1; + Module->CheckSum = NTHeaders->OptionalHeader.CheckSum; + Module->TimeDateStamp = NTHeaders->FileHeader.TimeDateStamp; + + RtlCreateUnicodeString (&Module->FullDllName, + FullDosName); + RtlCreateUnicodeString (&Module->BaseDllName, + wcsrchr(FullDosName, L'\\') + 1); + DPRINT ("BaseDllName %wZ\n", &Module->BaseDllName); + + RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); + InsertTailList(&NtCurrentPeb()->Ldr->InLoadOrderModuleList, + &Module->InLoadOrderModuleList); + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + + return(Module); +} + + +static NTSTATUS +LdrpMapKnownDll(IN PUNICODE_STRING DllName, + OUT PUNICODE_STRING FullDosName, + OUT PHANDLE SectionHandle) { - WCHAR SearchPathBuffer[MAX_PATH]; - WCHAR FullDosName[MAX_PATH]; - UNICODE_STRING AdjustedName; - UNICODE_STRING FullNtFileName; - OBJECT_ATTRIBUTES FileObjectAttributes; - char BlockBuffer [1024]; - PIMAGE_DOS_HEADER DosHeader; - NTSTATUS Status; - PIMAGE_NT_HEADERS NTHeaders; - ULONG ImageSize; - ULONG InitialViewSize; - PVOID ImageBase; - HANDLE FileHandle; - HANDLE SectionHandle; - PDLLMAIN_FUNC Entrypoint = NULL; - PLDR_MODULE Module; - - if (Name == NULL) + OBJECT_ATTRIBUTES ObjectAttributes; + NTSTATUS Status; + + DPRINT("LdrpMapKnownDll() called\n"); + + if (LdrpKnownDllsDirHandle == NULL) { - *BaseAddress = NtCurrentPeb()->ImageBaseAddress; - return STATUS_SUCCESS; + DPRINT("Invalid 'KnownDlls' directory\n"); + return STATUS_UNSUCCESSFUL; } - - *BaseAddress = NULL; - - DPRINT("LdrLoadDll(Name \"%wZ\" BaseAddress %x)\n", - Name, BaseAddress); - - /* adjust the full dll name */ - LdrAdjustDllName (&AdjustedName, - Name, - FALSE); - DPRINT("AdjustedName: %wZ\n", &AdjustedName); - - /* - * Test if dll is already loaded. - */ - if (LdrFindDll(&Module, &AdjustedName) == STATUS_SUCCESS) + + DPRINT("LdrpKnownDllPath '%wZ'\n", &LdrpKnownDllPath); + + InitializeObjectAttributes(&ObjectAttributes, + DllName, + OBJ_CASE_INSENSITIVE, + LdrpKnownDllsDirHandle, + NULL); + Status = NtOpenSection(SectionHandle, + SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT("NtOpenSection() failed for '%wZ' (Status %lx)\n", DllName, Status); + return Status; + } + + FullDosName->Length = LdrpKnownDllPath.Length + DllName->Length + sizeof(WCHAR); + FullDosName->MaximumLength = FullDosName->Length + sizeof(WCHAR); + FullDosName->Buffer = RtlAllocateHeap(RtlGetProcessHeap(), + 0, + FullDosName->MaximumLength); + if (FullDosName->Buffer == NULL) { - DPRINT("DLL %wZ already loaded.\n", &AdjustedName); - if (Module->LoadCount != -1) - Module->LoadCount++; - *BaseAddress = Module->BaseAddress; + FullDosName->Length = 0; + FullDosName->MaximumLength = 0; return STATUS_SUCCESS; } - DPRINT("Loading \"%wZ\"\n", Name); - + + wcscpy(FullDosName->Buffer, LdrpKnownDllPath.Buffer); + wcscat(FullDosName->Buffer, L"\\"); + wcscat(FullDosName->Buffer, DllName->Buffer); + + DPRINT("FullDosName '%wZ'\n", FullDosName); + + DPRINT("LdrpMapKnownDll() done\n"); + + return STATUS_SUCCESS; +} + + +static NTSTATUS +LdrpMapDllImageFile(IN PWSTR SearchPath OPTIONAL, + IN PUNICODE_STRING DllName, + OUT PUNICODE_STRING FullDosName, + IN BOOLEAN MapAsDataFile, + OUT PHANDLE SectionHandle) +{ + WCHAR SearchPathBuffer[MAX_PATH]; + WCHAR DosName[MAX_PATH]; + UNICODE_STRING FullNtFileName; + OBJECT_ATTRIBUTES FileObjectAttributes; + HANDLE FileHandle; + char BlockBuffer [1024]; + PIMAGE_DOS_HEADER DosHeader; + PIMAGE_NT_HEADERS NTHeaders; + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS Status; + ULONG len; + + DPRINT("LdrpMapDllImageFile() called\n"); + if (SearchPath == NULL) { - PKUSER_SHARED_DATA SharedUserData = - (PKUSER_SHARED_DATA)USER_SHARED_DATA_BASE; - - SearchPath = SearchPathBuffer; - wcscpy (SearchPathBuffer, SharedUserData->NtSystemRoot); + /* get application running path */ + + wcscpy (SearchPathBuffer, NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer); + + len = wcslen (SearchPathBuffer); + + while (len && SearchPathBuffer[len - 1] != L'\\') + len--; + + if (len) SearchPathBuffer[len-1] = L'\0'; + + wcscat (SearchPathBuffer, L";"); + + wcscat (SearchPathBuffer, SharedUserData->NtSystemRoot); wcscat (SearchPathBuffer, L"\\system32;"); wcscat (SearchPathBuffer, SharedUserData->NtSystemRoot); + wcscat (SearchPathBuffer, L";."); + + SearchPath = SearchPathBuffer; } - DPRINT("SearchPath %S\n", SearchPath); - if (RtlDosSearchPath_U (SearchPath, - AdjustedName.Buffer, - NULL, - MAX_PATH, - FullDosName, - NULL) == 0) + DllName->Buffer, + NULL, + MAX_PATH, + DosName, + NULL) == 0) return STATUS_DLL_NOT_FOUND; - - DPRINT("FullDosName %S\n", FullDosName); - - RtlFreeUnicodeString (&AdjustedName); - - if (!RtlDosPathNameToNtPathName_U (FullDosName, - &FullNtFileName, - NULL, - NULL)) + + + if (!RtlDosPathNameToNtPathName_U (DosName, + &FullNtFileName, + NULL, + NULL)) return STATUS_DLL_NOT_FOUND; - + DPRINT("FullNtFileName %wZ\n", &FullNtFileName); - + InitializeObjectAttributes(&FileObjectAttributes, - &FullNtFileName, - 0, - NULL, - NULL); - + &FullNtFileName, + 0, + NULL, + NULL); + DPRINT("Opening dll \"%wZ\"\n", &FullNtFileName); - - Status = ZwOpenFile(&FileHandle, - FILE_ALL_ACCESS, - &FileObjectAttributes, - NULL, - 0, - 0); + + Status = NtOpenFile(&FileHandle, + GENERIC_READ|SYNCHRONIZE, + &FileObjectAttributes, + &IoStatusBlock, + FILE_SHARE_READ, + FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(Status)) { - DbgPrint("Dll open of %wZ failed: Status = 0x%08x\n", - &FullNtFileName, Status); + DPRINT1("Dll open of %wZ failed: Status = 0x%08x\n", + &FullNtFileName, Status); RtlFreeUnicodeString (&FullNtFileName); return Status; } RtlFreeUnicodeString (&FullNtFileName); - - Status = ZwReadFile(FileHandle, - 0, - 0, - 0, - 0, - BlockBuffer, - sizeof(BlockBuffer), - 0, - 0); + + Status = NtReadFile(FileHandle, + NULL, + NULL, + NULL, + &IoStatusBlock, + BlockBuffer, + sizeof(BlockBuffer), + NULL, + NULL); if (!NT_SUCCESS(Status)) { DPRINT("Dll header read failed: Status = 0x%08x\n", Status); - ZwClose(FileHandle); + NtClose(FileHandle); return Status; } /* - * Overlay DOS and NT headers structures to the + * Overlay DOS and NT headers structures to the * buffer with DLL's header raw data. */ DosHeader = (PIMAGE_DOS_HEADER) BlockBuffer; @@ -299,152 +670,98 @@ LdrLoadDll (IN PWSTR SearchPath OPTIONAL, /* * Check it is a PE image file. */ - if ((DosHeader->e_magic != IMAGE_DOS_MAGIC) + if ((DosHeader->e_magic != IMAGE_DOS_SIGNATURE) || (DosHeader->e_lfanew == 0L) - || (*(PULONG)(NTHeaders) != IMAGE_PE_MAGIC)) + || (*(PULONG)(NTHeaders) != IMAGE_NT_SIGNATURE)) { DPRINT("NTDLL format invalid\n"); - ZwClose(FileHandle); - + NtClose(FileHandle); + return STATUS_UNSUCCESSFUL; } - - ImageBase = (PVOID) NTHeaders->OptionalHeader.ImageBase; - ImageSize = NTHeaders->OptionalHeader.SizeOfImage; - - DPRINT("ImageBase 0x%08x\n", ImageBase); - + /* * Create a section for dll. */ - Status = ZwCreateSection(&SectionHandle, - SECTION_ALL_ACCESS, - NULL, - NULL, - PAGE_READWRITE, - SEC_COMMIT | SEC_IMAGE, - FileHandle); + Status = NtCreateSection(SectionHandle, + SECTION_ALL_ACCESS, + NULL, + NULL, + PAGE_READONLY, + SEC_COMMIT | (MapAsDataFile ? 0 : SEC_IMAGE), + FileHandle); + NtClose(FileHandle); + if (!NT_SUCCESS(Status)) { DPRINT("NTDLL create section failed: Status = 0x%08x\n", Status); - ZwClose(FileHandle); return Status; } - - /* - * Map the dll into the process. - */ - InitialViewSize = 0; - ImageBase = 0; - Status = ZwMapViewOfSection(SectionHandle, - NtCurrentProcess(), - &ImageBase, - 0, - InitialViewSize, - NULL, - &InitialViewSize, - 0, - MEM_COMMIT, - PAGE_READWRITE); - if (!NT_SUCCESS(Status)) - { - DbgPrint("NTDLL.LDR: map view of section failed (Status %x)\n", - Status); - ZwClose(FileHandle); - return(Status); - } - ZwClose(FileHandle); - /* relocate dll and fixup import table */ - if ((NTHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == - IMAGE_FILE_DLL) - { - Entrypoint = - (PDLLMAIN_FUNC) LdrPEStartup(ImageBase, SectionHandle); - } - - /* build module entry */ - Module = RtlAllocateHeap(RtlGetProcessHeap(), - 0, - sizeof (LDR_MODULE)); - Module->BaseAddress = (PVOID)ImageBase; - Module->EntryPoint = NTHeaders->OptionalHeader.AddressOfEntryPoint; - if (Module->EntryPoint != 0) - Module->EntryPoint += (ULONG)Module->BaseAddress; - Module->SizeOfImage = ImageSize; - if (NtCurrentPeb()->Ldr->Initialized == TRUE) - { - /* loading while app is running */ - Module->LoadCount = 1; - } - else - { - /* - * loading while app is initializing - * dll must not be unloaded - */ - Module->LoadCount = -1; - } + RtlCreateUnicodeString(FullDosName, + DosName); - Module->TlsIndex = 0; - Module->CheckSum = NTHeaders->OptionalHeader.CheckSum; - Module->TimeDateStamp = NTHeaders->FileHeader.TimeDateStamp; + return Status; +} - RtlCreateUnicodeString (&Module->FullDllName, - FullDosName); - RtlCreateUnicodeString (&Module->BaseDllName, - wcsrchr(FullDosName, L'\\') + 1); - DPRINT ("BaseDllName %wZ\n", &Module->BaseDllName); - - /* FIXME: aquire loader lock */ - InsertTailList(&NtCurrentPeb()->Ldr->InLoadOrderModuleList, - &Module->InLoadOrderModuleList); - InsertTailList(&NtCurrentPeb()->Ldr->InInitializationOrderModuleList, - &Module->InInitializationOrderModuleList); - /* FIXME: release loader lock */ - -#ifdef KDBG - LdrLoadModuleSymbols(Module); -#endif /* KDBG */ - - /* initialize dll */ - if ((NTHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == - IMAGE_FILE_DLL) + + +/*************************************************************************** + * NAME EXPORTED + * LdrLoadDll + * + * DESCRIPTION + * + * ARGUMENTS + * + * RETURN VALUE + * + * REVISIONS + * + * NOTE + * + * @implemented + */ +NTSTATUS NTAPI +LdrLoadDll (IN PWSTR SearchPath OPTIONAL, + IN ULONG LoadFlags, + IN PUNICODE_STRING Name, + OUT PVOID *BaseAddress OPTIONAL) +{ + NTSTATUS Status; + PLDR_DATA_TABLE_ENTRY Module; + + TRACE_LDR("LdrLoadDll, loading %wZ%s%S\n", + Name, + SearchPath ? " from " : "", + SearchPath ? SearchPath : L""); + + if (Name == NULL) { - if (Module->EntryPoint != 0) - { - Entrypoint = (PDLLMAIN_FUNC)Module->EntryPoint; - - DPRINT("Calling entry point at 0x%08x\n", Entrypoint); - if (FALSE == Entrypoint(Module->BaseAddress, - DLL_PROCESS_ATTACH, - NULL)) - { - DPRINT("NTDLL.LDR: DLL \"%wZ\" failed to initialize\n", - &Module->BaseDllName); - /* FIXME: should clean up and fail */ - } - else - { - DPRINT("NTDLL.LDR: DLL \"%wZ\" initialized successfully\n", - &Module->BaseDllName); - } - } - else - { - DPRINT("NTDLL.LDR: Entrypoint is NULL for \"%wZ\"\n", - &Module->BaseDllName); - } + *BaseAddress = NtCurrentPeb()->ImageBaseAddress; + return STATUS_SUCCESS; } - *BaseAddress = Module->BaseAddress; - return STATUS_SUCCESS; + *BaseAddress = NULL; + + Status = LdrpLoadModule(SearchPath, LoadFlags, Name, &Module, BaseAddress); + if (NT_SUCCESS(Status) && 0 == (LoadFlags & LOAD_LIBRARY_AS_DATAFILE)) + { + RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); + Status = LdrpAttachProcess(); + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + if (NT_SUCCESS(Status)) + { + *BaseAddress = Module->DllBase; + } + } + return Status; } /*************************************************************************** - * NAME LOCAL - * LdrFindDll + * NAME EXPORTED + * LdrFindEntryForAddress * * DESCRIPTION * @@ -456,48 +773,57 @@ LdrLoadDll (IN PWSTR SearchPath OPTIONAL, * * NOTE * + * @implemented */ -static NTSTATUS LdrFindDll(PLDR_MODULE *Dll, PUNICODE_STRING Name) +NTSTATUS NTAPI +LdrFindEntryForAddress(PVOID Address, + PLDR_DATA_TABLE_ENTRY *Module) { - PLIST_ENTRY ModuleListHead; - PLIST_ENTRY Entry; - PLDR_MODULE Module; + PLIST_ENTRY ModuleListHead; + PLIST_ENTRY Entry; + PLDR_DATA_TABLE_ENTRY ModulePtr; - DPRINT("NTDLL.LdrFindDll(Name %wZ)\n", Name); + DPRINT("LdrFindEntryForAddress(Address %p)\n", Address); - ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; - Entry = ModuleListHead->Flink; + if (NtCurrentPeb()->Ldr == NULL) + return(STATUS_NO_MORE_ENTRIES); - // NULL is the current process - if ( Name == NULL ) - { - *Dll = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList); - return STATUS_SUCCESS; - } + RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); + ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; + Entry = ModuleListHead->Flink; + if (Entry == ModuleListHead) + { + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + return(STATUS_NO_MORE_ENTRIES); + } - while (Entry != ModuleListHead) - { - Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList); + while (Entry != ModuleListHead) + { + ModulePtr = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList); - DPRINT("Scanning %wZ %wZ\n", &Module->BaseDllName, Name); + DPRINT("Scanning %wZ at %p\n", &ModulePtr->BaseDllName, ModulePtr->DllBase); - if (RtlCompareUnicodeString(&Module->BaseDllName, Name, TRUE) == 0) - { - *Dll = Module; - return STATUS_SUCCESS; - } + if ((Address >= ModulePtr->DllBase) && + ((ULONG_PTR)Address <= ((ULONG_PTR)ModulePtr->DllBase + ModulePtr->SizeOfImage))) + { + *Module = ModulePtr; + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + return(STATUS_SUCCESS); + } - Entry = Entry->Flink; - } + Entry = Entry->Flink; + } - DPRINT("Failed to find dll %wZ\n", Name); + DPRINT("Failed to find module entry.\n"); - return STATUS_UNSUCCESSFUL; + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + return(STATUS_NO_MORE_ENTRIES); } -/********************************************************************** - * NAME LOCAL - * LdrFixupForward + +/*************************************************************************** + * NAME LOCAL + * LdrFindEntryForName * * DESCRIPTION * @@ -510,45 +836,156 @@ static NTSTATUS LdrFindDll(PLDR_MODULE *Dll, PUNICODE_STRING Name) * NOTE * */ -static PVOID -LdrFixupForward(PCHAR ForwardName) +static NTSTATUS +LdrFindEntryForName(PUNICODE_STRING Name, + PLDR_DATA_TABLE_ENTRY *Module, + BOOLEAN Ref) { - CHAR NameBuffer[128]; - UNICODE_STRING DllName; - UNICODE_STRING FunctionName; - NTSTATUS Status; - PCHAR p; - PVOID BaseAddress; + PLIST_ENTRY ModuleListHead; + PLIST_ENTRY Entry; + PLDR_DATA_TABLE_ENTRY ModulePtr; + BOOLEAN ContainsPath; + UNICODE_STRING AdjustedName; + unsigned i; + + DPRINT("LdrFindEntryForName(Name %wZ)\n", Name); + + if (NtCurrentPeb()->Ldr == NULL) + return(STATUS_NO_MORE_ENTRIES); + + RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); + ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; + Entry = ModuleListHead->Flink; + if (Entry == ModuleListHead) + { + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + return(STATUS_NO_MORE_ENTRIES); + } + + // NULL is the current process + if (Name == NULL) + { + *Module = ExeModule; + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + return(STATUS_SUCCESS); + } + + LdrAdjustDllName (&AdjustedName, Name, FALSE); + + ContainsPath = (AdjustedName.Length >= 2 * sizeof(WCHAR) && L':' == AdjustedName.Buffer[1]); + for (i = 0; ! ContainsPath && i < AdjustedName.Length / sizeof(WCHAR); i++) + { + ContainsPath = L'\\' == AdjustedName.Buffer[i] || + L'/' == AdjustedName.Buffer[i]; + } + + if (LdrpLastModule) + { + if ((! ContainsPath && + 0 == RtlCompareUnicodeString(&LdrpLastModule->BaseDllName, &AdjustedName, TRUE)) || + (ContainsPath && + 0 == RtlCompareUnicodeString(&LdrpLastModule->FullDllName, &AdjustedName, TRUE))) + { + *Module = LdrpLastModule; + if (Ref && (*Module)->LoadCount != 0xFFFF) + { + (*Module)->LoadCount++; + } + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + RtlFreeUnicodeString(&AdjustedName); + return(STATUS_SUCCESS); + } + } + while (Entry != ModuleListHead) + { + ModulePtr = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList); + + DPRINT("Scanning %wZ %wZ\n", &ModulePtr->BaseDllName, &AdjustedName); + + if ((! ContainsPath && + 0 == RtlCompareUnicodeString(&ModulePtr->BaseDllName, &AdjustedName, TRUE)) || + (ContainsPath && + 0 == RtlCompareUnicodeString(&ModulePtr->FullDllName, &AdjustedName, TRUE))) + { + *Module = LdrpLastModule = ModulePtr; + if (Ref && ModulePtr->LoadCount != 0xFFFF) + { + ModulePtr->LoadCount++; + } + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + RtlFreeUnicodeString(&AdjustedName); + return(STATUS_SUCCESS); + } + + Entry = Entry->Flink; + } + + DPRINT("Failed to find dll %wZ\n", Name); + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + RtlFreeUnicodeString(&AdjustedName); + return(STATUS_NO_MORE_ENTRIES); +} + +/********************************************************************** + * NAME LOCAL + * LdrFixupForward + * + * DESCRIPTION + * + * ARGUMENTS + * + * RETURN VALUE + * + * REVISIONS + * + * NOTE + * + */ +static PVOID +LdrFixupForward(PCHAR ForwardName) +{ + CHAR NameBuffer[128]; + UNICODE_STRING DllName; + NTSTATUS Status; + PCHAR p; + PLDR_DATA_TABLE_ENTRY Module; + PVOID BaseAddress; strcpy(NameBuffer, ForwardName); p = strchr(NameBuffer, '.'); if (p != NULL) { - *p = 0; - - DPRINT("Dll: %s Function: %s\n", NameBuffer, p+1); - RtlCreateUnicodeStringFromAsciiz (&DllName, - NameBuffer); - - Status = LdrGetDllHandle (0, 0, &DllName, &BaseAddress); - if (!NT_SUCCESS(Status)) - { - Status = LdrLoadDll(NULL, - 0, - &DllName, - &BaseAddress); - if (!NT_SUCCESS(Status)) - { - DbgPrint("LdrFixupForward: failed to load %wZ\n", &DllName); - RtlFreeUnicodeString (&DllName); - return NULL; - } - } - - RtlFreeUnicodeString (&DllName); - DPRINT("BaseAddress: %p\n", BaseAddress); - - return LdrGetExportByName(BaseAddress, p+1, -1); + *p = 0; + + DPRINT("Dll: %s Function: %s\n", NameBuffer, p+1); + RtlCreateUnicodeStringFromAsciiz (&DllName, + NameBuffer); + + Status = LdrFindEntryForName (&DllName, &Module, FALSE); + /* FIXME: + * The caller (or the image) is responsible for loading of the dll, where the function is forwarded. + */ + if (!NT_SUCCESS(Status)) + { + Status = LdrLoadDll(NULL, + LDRP_PROCESS_CREATION_TIME, + &DllName, + &BaseAddress); + if (NT_SUCCESS(Status)) + { + Status = LdrFindEntryForName (&DllName, &Module, FALSE); + } + } + RtlFreeUnicodeString (&DllName); + if (!NT_SUCCESS(Status)) + { + DPRINT1("LdrFixupForward: failed to load %s\n", NameBuffer); + return NULL; + } + + DPRINT("BaseAddress: %p\n", Module->DllBase); + + return LdrGetExportByName(Module->DllBase, (PUCHAR)(p+1), -1); } return NULL; @@ -556,9 +993,9 @@ LdrFixupForward(PCHAR ForwardName) /********************************************************************** - * NAME LOCAL - * LdrGetExportByOrdinal - * + * NAME LOCAL + * LdrGetExportByOrdinal + * * DESCRIPTION * * ARGUMENTS @@ -572,44 +1009,52 @@ LdrFixupForward(PCHAR ForwardName) */ static PVOID LdrGetExportByOrdinal ( - PVOID BaseAddress, - ULONG Ordinal - ) + PVOID BaseAddress, + ULONG Ordinal + ) { - PIMAGE_EXPORT_DIRECTORY ExportDir; - PDWORD * ExFunctions; - USHORT * ExOrdinals; - - ExportDir = (PIMAGE_EXPORT_DIRECTORY) - RtlImageDirectoryEntryToData (BaseAddress, - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - NULL); - - - ExOrdinals = (USHORT *) - RVA( - BaseAddress, - ExportDir->AddressOfNameOrdinals - ); - ExFunctions = (PDWORD *) - RVA( - BaseAddress, - ExportDir->AddressOfFunctions - ); - DbgPrint( - "LdrGetExportByOrdinal(Ordinal %d) = %x\n", - Ordinal, - ExFunctions[ExOrdinals[Ordinal - ExportDir->Base]] - ); - return(ExFunctions[ExOrdinals[Ordinal - ExportDir->Base]]); + PIMAGE_EXPORT_DIRECTORY ExportDir; + ULONG ExportDirSize; + PDWORD * ExFunctions; + PVOID Function; + + ExportDir = (PIMAGE_EXPORT_DIRECTORY) + RtlImageDirectoryEntryToData (BaseAddress, + TRUE, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &ExportDirSize); + + + ExFunctions = (PDWORD *) + RVA( + BaseAddress, + ExportDir->AddressOfFunctions + ); + DPRINT( + "LdrGetExportByOrdinal(Ordinal %d) = %p\n", + Ordinal, + RVA(BaseAddress, ExFunctions[Ordinal - ExportDir->Base] ) + ); + + Function = (0 != ExFunctions[Ordinal - ExportDir->Base] + ? RVA(BaseAddress, ExFunctions[Ordinal - ExportDir->Base] ) + : NULL); + + if (((ULONG)Function >= (ULONG)ExportDir) && + ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize)) + { + DPRINT("Forward: %s\n", (PCHAR)Function); + Function = LdrFixupForward((PCHAR)Function); + } + + return Function; } /********************************************************************** - * NAME LOCAL - * LdrGetExportByName - * + * NAME LOCAL + * LdrGetExportByName + * * DESCRIPTION * * ARGUMENTS @@ -619,143 +1064,141 @@ LdrGetExportByOrdinal ( * REVISIONS * * NOTE + * AddressOfNames and AddressOfNameOrdinals are paralell tables, + * both with NumberOfNames entries. * */ static PVOID LdrGetExportByName(PVOID BaseAddress, - PUCHAR SymbolName, - WORD Hint) + PUCHAR SymbolName, + WORD Hint) { - PIMAGE_EXPORT_DIRECTORY ExportDir; - PDWORD * ExFunctions; - PDWORD * ExNames; - USHORT * ExOrdinals; - ULONG i; - PVOID ExName; - ULONG Ordinal; - PVOID Function; - ULONG minn, maxn; + PIMAGE_EXPORT_DIRECTORY ExportDir; + PDWORD * ExFunctions; + PDWORD * ExNames; + USHORT * ExOrdinals; + PVOID ExName; + ULONG Ordinal; + PVOID Function; + LONG minn, maxn; ULONG ExportDirSize; - + DPRINT("LdrGetExportByName %x %s %hu\n", BaseAddress, SymbolName, Hint); - + ExportDir = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData(BaseAddress, - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &ExportDirSize); + TRUE, + IMAGE_DIRECTORY_ENTRY_EXPORT, + &ExportDirSize); if (ExportDir == NULL) { - DbgPrint("LdrGetExportByName(): no export directory!\n"); - return NULL; + DPRINT1("LdrGetExportByName(): no export directory!\n"); + return NULL; } - + + + //The symbol names may be missing entirely + if (ExportDir->AddressOfNames == 0) + { + DPRINT("LdrGetExportByName(): symbol names missing entirely\n"); + return NULL; + } + /* * Get header pointers */ ExNames = (PDWORD *)RVA(BaseAddress, - ExportDir->AddressOfNames); + ExportDir->AddressOfNames); ExOrdinals = (USHORT *)RVA(BaseAddress, - ExportDir->AddressOfNameOrdinals); + ExportDir->AddressOfNameOrdinals); ExFunctions = (PDWORD *)RVA(BaseAddress, - ExportDir->AddressOfFunctions); - + ExportDir->AddressOfFunctions); + /* * Check the hint first */ - if (Hint < ExportDir->NumberOfFunctions) + if (Hint < ExportDir->NumberOfNames) { - ExName = RVA(BaseAddress, ExNames[Hint]); - if (strcmp(ExName, SymbolName) == 0) - { - Ordinal = ExOrdinals[Hint]; - Function = RVA(BaseAddress, ExFunctions[Ordinal]); - if (((ULONG)Function >= (ULONG)ExportDir) && - ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize)) - { - DPRINT("Forward: %s\n", (PCHAR)Function); - Function = LdrFixupForward((PCHAR)Function); - } - if (Function != NULL) - return Function; - } + ExName = RVA(BaseAddress, ExNames[Hint]); + if (strcmp(ExName, (PCHAR)SymbolName) == 0) + { + Ordinal = ExOrdinals[Hint]; + Function = RVA(BaseAddress, ExFunctions[Ordinal]); + if (((ULONG)Function >= (ULONG)ExportDir) && + ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize)) + { + DPRINT("Forward: %s\n", (PCHAR)Function); + Function = LdrFixupForward((PCHAR)Function); + if (Function == NULL) + { + DPRINT1("LdrGetExportByName(): failed to find %s\n",SymbolName); + } + return Function; + } + if (Function != NULL) + return Function; + } } - + /* - * Try a binary search first + * Binary search */ minn = 0; - maxn = ExportDir->NumberOfFunctions; + maxn = ExportDir->NumberOfNames - 1; while (minn <= maxn) { - ULONG mid; - LONG res; - - mid = (minn + maxn) / 2; - - ExName = RVA(BaseAddress, ExNames[mid]); - res = strcmp(ExName, SymbolName); - if (res == 0) - { - Ordinal = ExOrdinals[mid]; - Function = RVA(BaseAddress, ExFunctions[Ordinal]); - if (((ULONG)Function >= (ULONG)ExportDir) && - ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize)) - { - DPRINT("Forward: %s\n", (PCHAR)Function); - Function = LdrFixupForward((PCHAR)Function); - } - if (Function != NULL) - return Function; - } - else if (minn == maxn) - { - DPRINT("LdrGetExportByName(): binary search failed\n"); - break; - } - else if (res > 0) - { - maxn = mid - 1; - } - else - { - minn = mid + 1; - } - } - - /* - * Fall back on a linear search - */ - DPRINT("LdrGetExportByName(): Falling back on a linear search of export table\n"); - for (i = 0; i < ExportDir->NumberOfFunctions; i++) - { - ExName = RVA(BaseAddress, ExNames[i]); - if (strcmp(ExName,SymbolName) == 0) - { - Ordinal = ExOrdinals[i]; - Function = RVA(BaseAddress, ExFunctions[Ordinal]); - DPRINT("%x %x %x\n", Function, ExportDir, ExportDir + ExportDirSize); - if (((ULONG)Function >= (ULONG)ExportDir) && - ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize)) - { - DPRINT("Forward: %s\n", (PCHAR)Function); - Function = LdrFixupForward((PCHAR)Function); - } - return Function; - } + LONG mid; + LONG res; + + mid = (minn + maxn) / 2; + + ExName = RVA(BaseAddress, ExNames[mid]); + res = strcmp(ExName, (PCHAR)SymbolName); + if (res == 0) + { + Ordinal = ExOrdinals[mid]; + Function = RVA(BaseAddress, ExFunctions[Ordinal]); + if (((ULONG)Function >= (ULONG)ExportDir) && + ((ULONG)Function < (ULONG)ExportDir + (ULONG)ExportDirSize)) + { + DPRINT("Forward: %s\n", (PCHAR)Function); + Function = LdrFixupForward((PCHAR)Function); + if (Function == NULL) + { + DPRINT1("LdrGetExportByName(): failed to find %s\n",SymbolName); + } + return Function; + } + if (Function != NULL) + return Function; + } + else if (minn == maxn) + { + DPRINT("LdrGetExportByName(): binary search failed\n"); + break; + } + else if (res > 0) + { + maxn = mid - 1; + } + else + { + minn = mid + 1; + } } - DbgPrint("LdrGetExportByName(): failed to find %s\n",SymbolName); - return NULL; + + DPRINT1("LdrGetExportByName(): failed to find %s\n",SymbolName); + return (PVOID)NULL; } /********************************************************************** - * NAME LOCAL - * LdrPerformRelocations - * + * NAME LOCAL + * LdrPerformRelocations + * * DESCRIPTION - * Relocate a DLL's memory image. - * + * Relocate a DLL's memory image. + * * ARGUMENTS * * RETURN VALUE @@ -765,109 +1208,399 @@ LdrGetExportByName(PVOID BaseAddress, * NOTE * */ -static NTSTATUS LdrPerformRelocations (PIMAGE_NT_HEADERS NTHeaders, - PVOID ImageBase) +static NTSTATUS +LdrPerformRelocations(PIMAGE_NT_HEADERS NTHeaders, + PVOID ImageBase) { - USHORT NumberOfEntries; - PUSHORT pValue16; - ULONG RelocationRVA; - ULONG Delta32; - ULONG Offset; - PULONG pValue32; - PRELOCATION_DIRECTORY RelocationDir; - PRELOCATION_ENTRY RelocationBlock; - int i; - - - RelocationRVA = NTHeaders->OptionalHeader - .DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC] - .VirtualAddress; - - if (RelocationRVA) - { - RelocationDir = (PRELOCATION_DIRECTORY) - ((PCHAR)ImageBase + RelocationRVA); - - while (RelocationDir->SizeOfBlock) - { - Delta32 = (ULONG)(ImageBase - - NTHeaders->OptionalHeader.ImageBase); - RelocationBlock = (PRELOCATION_ENTRY) ( - RelocationRVA - + ImageBase - + sizeof (RELOCATION_DIRECTORY) - ); - NumberOfEntries = ( - RelocationDir->SizeOfBlock - - sizeof (RELOCATION_DIRECTORY) - ) - / sizeof (RELOCATION_ENTRY); - - for ( i = 0; - (i < NumberOfEntries); - i++ - ) - { - Offset = ( - RelocationBlock[i].TypeOffset - & 0xfff - ) - + RelocationDir->VirtualAddress; - /* - * What kind of relocations should we perform - * for the current entry? - */ - switch (RelocationBlock[i].TypeOffset >> 12) - { - case TYPE_RELOC_ABSOLUTE: - break; - - case TYPE_RELOC_HIGH: - pValue16 = (PUSHORT) (ImageBase + Offset); - *pValue16 += Delta32 >> 16; - break; - - case TYPE_RELOC_LOW: - pValue16 = (PUSHORT)(ImageBase + Offset); - *pValue16 += Delta32 & 0xffff; - break; - - case TYPE_RELOC_HIGHLOW: - pValue32 = (PULONG) (ImageBase + Offset); - *pValue32 += Delta32; - break; - - case TYPE_RELOC_HIGHADJ: - /* FIXME: do the highadjust fixup */ - DPRINT( - "TYPE_RELOC_HIGHADJ fixup not implemented" - ", sorry\n" - ); - return(STATUS_UNSUCCESSFUL); - - default: - DPRINT("unexpected fixup type\n"); - return STATUS_UNSUCCESSFUL; - } - } - RelocationRVA += RelocationDir->SizeOfBlock; - RelocationDir = (PRELOCATION_DIRECTORY) ( - ImageBase - + RelocationRVA - ); - } - } + PIMAGE_DATA_DIRECTORY RelocationDDir; + PIMAGE_BASE_RELOCATION RelocationDir, RelocationEnd; + ULONG Count, ProtectSize, OldProtect, OldProtect2; + PVOID Page, ProtectPage, ProtectPage2; + PUSHORT TypeOffset; + ULONG_PTR Delta; + NTSTATUS Status; + + if (NTHeaders->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) + { + return STATUS_UNSUCCESSFUL; + } + + RelocationDDir = + &NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; + + if (RelocationDDir->VirtualAddress == 0 || RelocationDDir->Size == 0) + { + return STATUS_SUCCESS; + } + + ProtectSize = PAGE_SIZE; + Delta = (ULONG_PTR)ImageBase - NTHeaders->OptionalHeader.ImageBase; + RelocationDir = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)ImageBase + + RelocationDDir->VirtualAddress); + RelocationEnd = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)ImageBase + + RelocationDDir->VirtualAddress + RelocationDDir->Size); + + while (RelocationDir < RelocationEnd && + RelocationDir->SizeOfBlock > 0) + { + Count = (RelocationDir->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / + sizeof(USHORT); + Page = (PVOID)((ULONG_PTR)ImageBase + (ULONG_PTR)RelocationDir->VirtualAddress); + TypeOffset = (PUSHORT)(RelocationDir + 1); + + /* Unprotect the page(s) we're about to relocate. */ + ProtectPage = Page; + Status = NtProtectVirtualMemory(NtCurrentProcess(), + &ProtectPage, + &ProtectSize, + PAGE_READWRITE, + &OldProtect); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to unprotect relocation target.\n"); + return Status; + } + + if (RelocationDir->VirtualAddress + PAGE_SIZE < + NTHeaders->OptionalHeader.SizeOfImage) + { + ProtectPage2 = (PVOID)((ULONG_PTR)ProtectPage + PAGE_SIZE); + Status = NtProtectVirtualMemory(NtCurrentProcess(), + &ProtectPage2, + &ProtectSize, + PAGE_READWRITE, + &OldProtect2); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to unprotect relocation target (2).\n"); + NtProtectVirtualMemory(NtCurrentProcess(), + &ProtectPage, + &ProtectSize, + OldProtect, + &OldProtect); + return Status; + } + } + else + { + ProtectPage2 = NULL; + } + + RelocationDir = LdrProcessRelocationBlock(Page, + Count, + TypeOffset, + Delta); + if (RelocationDir == NULL) + return STATUS_UNSUCCESSFUL; + + /* Restore old page protection. */ + NtProtectVirtualMemory(NtCurrentProcess(), + &ProtectPage, + &ProtectSize, + OldProtect, + &OldProtect); + + if (ProtectPage2 != NULL) + { + NtProtectVirtualMemory(NtCurrentProcess(), + &ProtectPage2, + &ProtectSize, + OldProtect2, + &OldProtect2); + } + } + + return STATUS_SUCCESS; +} + +static NTSTATUS +LdrpGetOrLoadModule(PWCHAR SerachPath, + PCHAR Name, + PLDR_DATA_TABLE_ENTRY* Module, + BOOLEAN Load) +{ + UNICODE_STRING DllName; + NTSTATUS Status; + + DPRINT("LdrpGetOrLoadModule() called for %s\n", Name); + + RtlCreateUnicodeStringFromAsciiz (&DllName, Name); + + Status = LdrFindEntryForName (&DllName, Module, Load); + if (Load && !NT_SUCCESS(Status)) + { + Status = LdrpLoadModule(SerachPath, + NtCurrentPeb()->Ldr->Initialized ? 0 : LDRP_PROCESS_CREATION_TIME, + &DllName, + Module, + NULL); + if (NT_SUCCESS(Status)) + { + Status = LdrFindEntryForName (&DllName, Module, FALSE); + } + if (!NT_SUCCESS(Status)) + { + DPRINT1("failed to load %wZ\n", &DllName); + } + } + RtlFreeUnicodeString (&DllName); + return Status; +} + +static NTSTATUS +LdrpProcessImportDirectoryEntry(PLDR_DATA_TABLE_ENTRY Module, + PLDR_DATA_TABLE_ENTRY ImportedModule, + PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory) +{ + NTSTATUS Status; + PVOID* ImportAddressList; + PULONG FunctionNameList; + PVOID IATBase; + ULONG OldProtect; + ULONG Ordinal; + ULONG IATSize; + + if (ImportModuleDirectory == NULL || ImportModuleDirectory->Name == 0) + { + return STATUS_UNSUCCESSFUL; + } + + /* Get the import address list. */ + ImportAddressList = (PVOID *)((ULONG_PTR)Module->DllBase + (ULONG_PTR)ImportModuleDirectory->FirstThunk); + + /* Get the list of functions to import. */ + if (ImportModuleDirectory->OriginalFirstThunk != 0) + { + FunctionNameList = (PULONG) ((ULONG_PTR)Module->DllBase + (ULONG_PTR)ImportModuleDirectory->OriginalFirstThunk); + } + else + { + FunctionNameList = (PULONG)((ULONG_PTR)Module->DllBase + (ULONG_PTR)ImportModuleDirectory->FirstThunk); + } + + /* Get the size of IAT. */ + IATSize = 0; + while (FunctionNameList[IATSize] != 0L) + { + IATSize++; + } + + /* Unprotect the region we are about to write into. */ + IATBase = (PVOID)ImportAddressList; + IATSize *= sizeof(PVOID*); + Status = NtProtectVirtualMemory(NtCurrentProcess(), + &IATBase, + &IATSize, + PAGE_READWRITE, + &OldProtect); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to unprotect IAT.\n"); + return(Status); + } + + /* Walk through function list and fixup addresses. */ + while (*FunctionNameList != 0L) + { + if ((*FunctionNameList) & 0x80000000) + { + Ordinal = (*FunctionNameList) & 0x7fffffff; + *ImportAddressList = LdrGetExportByOrdinal(ImportedModule->DllBase, Ordinal); + if ((*ImportAddressList) == NULL) + { + DPRINT1("Failed to import #%ld from %wZ\n", Ordinal, &ImportedModule->FullDllName); + return STATUS_UNSUCCESSFUL; + } + } + else + { + IMAGE_IMPORT_BY_NAME *pe_name; + pe_name = RVA(Module->DllBase, *FunctionNameList); + *ImportAddressList = LdrGetExportByName(ImportedModule->DllBase, pe_name->Name, pe_name->Hint); + if ((*ImportAddressList) == NULL) + { + DPRINT1("Failed to import %s from %wZ\n", pe_name->Name, &ImportedModule->FullDllName); + return STATUS_UNSUCCESSFUL; + } + } + ImportAddressList++; + FunctionNameList++; + } + + /* Protect the region we are about to write into. */ + Status = NtProtectVirtualMemory(NtCurrentProcess(), + &IATBase, + &IATSize, + OldProtect, + &OldProtect); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to protect IAT.\n"); + return(Status); + } + + return STATUS_SUCCESS; +} + +static NTSTATUS +LdrpProcessImportDirectory( + PLDR_DATA_TABLE_ENTRY Module, + PLDR_DATA_TABLE_ENTRY ImportedModule, + PCHAR ImportedName) +{ + NTSTATUS Status; + PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory; + PCHAR Name; + ULONG Size; + + DPRINT("LdrpProcessImportDirectory(%x '%wZ', '%s')\n", + Module, &Module->BaseDllName, ImportedName); + + + ImportModuleDirectory = (PIMAGE_IMPORT_DESCRIPTOR) + RtlImageDirectoryEntryToData(Module->DllBase, + TRUE, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &Size); + if (ImportModuleDirectory == NULL) + { + return STATUS_UNSUCCESSFUL; + } + + while (ImportModuleDirectory->Name) + { + Name = (PCHAR)Module->DllBase + ImportModuleDirectory->Name; + if (0 == _stricmp(Name, ImportedName)) + { + Status = LdrpProcessImportDirectoryEntry(Module, + ImportedModule, + ImportModuleDirectory); + if (!NT_SUCCESS(Status)) + { + return Status; + } + } + ImportModuleDirectory++; + } + + + return STATUS_SUCCESS; +} + + +static NTSTATUS +LdrpAdjustImportDirectory(PLDR_DATA_TABLE_ENTRY Module, + PLDR_DATA_TABLE_ENTRY ImportedModule, + PCHAR ImportedName) +{ + PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory; + NTSTATUS Status; + PVOID* ImportAddressList; + PVOID Start; + PVOID End; + PULONG FunctionNameList; + PVOID IATBase; + ULONG OldProtect; + ULONG Offset; + ULONG IATSize; + PIMAGE_NT_HEADERS NTHeaders; + PCHAR Name; + ULONG Size; + + DPRINT("LdrpAdjustImportDirectory(Module %x '%wZ', %x '%wZ', %x '%s')\n", + Module, &Module->BaseDllName, ImportedModule, &ImportedModule->BaseDllName, ImportedName); + + ImportModuleDirectory = (PIMAGE_IMPORT_DESCRIPTOR) + RtlImageDirectoryEntryToData(Module->DllBase, + TRUE, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &Size); + if (ImportModuleDirectory == NULL) + { + return STATUS_UNSUCCESSFUL; + } + + while (ImportModuleDirectory->Name) + { + Name = (PCHAR)Module->DllBase + ImportModuleDirectory->Name; + if (0 == _stricmp(Name, (PCHAR)ImportedName)) + { + + /* Get the import address list. */ + ImportAddressList = (PVOID *)((ULONG_PTR)Module->DllBase + (ULONG_PTR)ImportModuleDirectory->FirstThunk); + + /* Get the list of functions to import. */ + if (ImportModuleDirectory->OriginalFirstThunk != 0) + { + FunctionNameList = (PULONG) ((ULONG_PTR)Module->DllBase + (ULONG_PTR)ImportModuleDirectory->OriginalFirstThunk); + } + else + { + FunctionNameList = (PULONG)((ULONG_PTR)Module->DllBase + (ULONG_PTR)ImportModuleDirectory->FirstThunk); + } + + /* Get the size of IAT. */ + IATSize = 0; + while (FunctionNameList[IATSize] != 0L) + { + IATSize++; + } + + /* Unprotect the region we are about to write into. */ + IATBase = (PVOID)ImportAddressList; + IATSize *= sizeof(PVOID*); + Status = NtProtectVirtualMemory(NtCurrentProcess(), + &IATBase, + &IATSize, + PAGE_READWRITE, + &OldProtect); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to unprotect IAT.\n"); + return(Status); + } + + NTHeaders = RtlImageNtHeader (ImportedModule->DllBase); + Start = (PVOID)NTHeaders->OptionalHeader.ImageBase; + End = (PVOID)((ULONG_PTR)Start + ImportedModule->SizeOfImage); + Offset = (ULONG)((ULONG_PTR)ImportedModule->DllBase - (ULONG_PTR)Start); + + /* Walk through function list and fixup addresses. */ + while (*FunctionNameList != 0L) + { + if (*ImportAddressList >= Start && *ImportAddressList < End) + { + (*ImportAddressList) = (PVOID)((ULONG_PTR)(*ImportAddressList) + Offset); + } + ImportAddressList++; + FunctionNameList++; + } + + /* Protect the region we are about to write into. */ + Status = NtProtectVirtualMemory(NtCurrentProcess(), + &IATBase, + &IATSize, + OldProtect, + &OldProtect); + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to protect IAT.\n"); + return(Status); + } + } + ImportModuleDirectory++; + } return STATUS_SUCCESS; } /********************************************************************** - * NAME LOCAL - * LdrFixupImports - * + * NAME LOCAL + * LdrFixupImports + * * DESCRIPTION - * Compute the entry point for every symbol the DLL imports - * from other modules. + * Compute the entry point for every symbol the DLL imports + * from other modules. * * ARGUMENTS * @@ -878,149 +1611,267 @@ static NTSTATUS LdrPerformRelocations (PIMAGE_NT_HEADERS NTHeaders, * NOTE * */ -static NTSTATUS LdrFixupImports(PIMAGE_NT_HEADERS NTHeaders, - PVOID ImageBase) +static NTSTATUS +LdrFixupImports(IN PWSTR SearchPath OPTIONAL, + IN PLDR_DATA_TABLE_ENTRY Module) { - PIMAGE_IMPORT_MODULE_DIRECTORY ImportModuleDirectory; - ULONG Ordinal; - PVOID BaseAddress; + PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory; + PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectoryCurrent; + PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundImportDescriptor; + PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundImportDescriptorCurrent; + PIMAGE_TLS_DIRECTORY TlsDirectory; + ULONG TlsSize = 0; NTSTATUS Status; - - DPRINT("LdrFixupImports(NTHeaders %x, ImageBase %x)\n", NTHeaders, - ImageBase); - + PLDR_DATA_TABLE_ENTRY ImportedModule; + PCHAR ImportedName; + ULONG Size; + + DPRINT("LdrFixupImports(SearchPath %x, Module %x)\n", SearchPath, Module); + + /* Check for tls data */ + TlsDirectory = (PIMAGE_TLS_DIRECTORY) + RtlImageDirectoryEntryToData(Module->DllBase, + TRUE, + IMAGE_DIRECTORY_ENTRY_TLS, + &Size); + if (TlsDirectory) + { + TlsSize = TlsDirectory->EndAddressOfRawData + - TlsDirectory->StartAddressOfRawData + + TlsDirectory->SizeOfZeroFill; + if (TlsSize > 0 && + NtCurrentPeb()->Ldr->Initialized) + { + TRACE_LDR("Trying to load dynamicly %wZ which contains a tls directory\n", + &Module->BaseDllName); + return STATUS_UNSUCCESSFUL; + } + } /* * Process each import module. */ - ImportModuleDirectory = (PIMAGE_IMPORT_MODULE_DIRECTORY)( - ImageBase + NTHeaders->OptionalHeader - .DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] - .VirtualAddress); - DPRINT("ImportModuleDirectory %x\n", ImportModuleDirectory); + ImportModuleDirectory = (PIMAGE_IMPORT_DESCRIPTOR) + RtlImageDirectoryEntryToData(Module->DllBase, + TRUE, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &Size); + + BoundImportDescriptor = (PIMAGE_BOUND_IMPORT_DESCRIPTOR) + RtlImageDirectoryEntryToData(Module->DllBase, + TRUE, + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, + &Size); + + if (BoundImportDescriptor != NULL && ImportModuleDirectory == NULL) + { + DPRINT1("%wZ has only a bound import directory\n", &Module->BaseDllName); + return STATUS_UNSUCCESSFUL; + } + if (BoundImportDescriptor) + { + DPRINT("BoundImportDescriptor %x\n", BoundImportDescriptor); + + BoundImportDescriptorCurrent = BoundImportDescriptor; + while (BoundImportDescriptorCurrent->OffsetModuleName) + { + ImportedName = (PCHAR)BoundImportDescriptor + BoundImportDescriptorCurrent->OffsetModuleName; + TRACE_LDR("%wZ bound to %s\n", &Module->BaseDllName, ImportedName); + Status = LdrpGetOrLoadModule(SearchPath, ImportedName, &ImportedModule, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("failed to load %s\n", ImportedName); + return Status; + } + if (Module == ImportedModule) + { + LdrpDecrementLoadCount(Module, FALSE); + } + if (ImportedModule->TimeDateStamp != BoundImportDescriptorCurrent->TimeDateStamp) + { + TRACE_LDR("%wZ has stale binding to %wZ\n", + &Module->BaseDllName, &ImportedModule->BaseDllName); + Status = LdrpProcessImportDirectory(Module, ImportedModule, ImportedName); + if (!NT_SUCCESS(Status)) + { + DPRINT1("failed to import %s\n", ImportedName); + return Status; + } + } + else + { + BOOLEAN WrongForwarder; + WrongForwarder = FALSE; + if (ImportedModule->Flags & LDRP_IMAGE_NOT_AT_BASE) + { + TRACE_LDR("%wZ has stale binding to %s\n", + &Module->BaseDllName, ImportedName); + } + else + { + TRACE_LDR("%wZ has correct binding to %wZ\n", + &Module->BaseDllName, &ImportedModule->BaseDllName); + } + if (BoundImportDescriptorCurrent->NumberOfModuleForwarderRefs) + { + PIMAGE_BOUND_FORWARDER_REF BoundForwarderRef; + ULONG i; + PLDR_DATA_TABLE_ENTRY ForwarderModule; + PCHAR ForwarderName; + + BoundForwarderRef = (PIMAGE_BOUND_FORWARDER_REF)(BoundImportDescriptorCurrent + 1); + for (i = 0; i < BoundImportDescriptorCurrent->NumberOfModuleForwarderRefs; i++, BoundForwarderRef++) + { + ForwarderName = (PCHAR)BoundImportDescriptor + BoundForwarderRef->OffsetModuleName; + TRACE_LDR("%wZ bound to %s via forwardes from %s\n", + &Module->BaseDllName, ForwarderName, ImportedName); + Status = LdrpGetOrLoadModule(SearchPath, ForwarderName, &ForwarderModule, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("failed to load %s\n", ForwarderName); + return Status; + } + if (Module == ImportedModule) + { + LdrpDecrementLoadCount(Module, FALSE); + } + if (ForwarderModule->TimeDateStamp != BoundForwarderRef->TimeDateStamp || + ForwarderModule->Flags & LDRP_IMAGE_NOT_AT_BASE) + { + TRACE_LDR("%wZ has stale binding to %s\n", + &Module->BaseDllName, ForwarderName); + WrongForwarder = TRUE; + } + else + { + TRACE_LDR("%wZ has correct binding to %s\n", + &Module->BaseDllName, ForwarderName); + } + } + } + if (WrongForwarder || + ImportedModule->Flags & LDRP_IMAGE_NOT_AT_BASE) + { + Status = LdrpProcessImportDirectory(Module, ImportedModule, ImportedName); + if (!NT_SUCCESS(Status)) + { + DPRINT1("failed to import %s\n", ImportedName); + return Status; + } + } + else if (ImportedModule->Flags & LDRP_IMAGE_NOT_AT_BASE) + { + TRACE_LDR("Adjust imports for %s from %wZ\n", + ImportedName, &Module->BaseDllName); + Status = LdrpAdjustImportDirectory(Module, ImportedModule, ImportedName); + if (!NT_SUCCESS(Status)) + { + DPRINT1("failed to adjust import entries for %s\n", ImportedName); + return Status; + } + } + else if (WrongForwarder) + { + /* + * FIXME: + * Update only forwarders + */ + TRACE_LDR("Stale BIND %s from %wZ\n", + ImportedName, &Module->BaseDllName); + Status = LdrpProcessImportDirectory(Module, ImportedModule, ImportedName); + if (!NT_SUCCESS(Status)) + { + DPRINT1("faild to import %s\n", ImportedName); + return Status; + } + } + else + { + /* nothing to do */ + } + } + BoundImportDescriptorCurrent += BoundImportDescriptorCurrent->NumberOfModuleForwarderRefs + 1; + } + } + else if (ImportModuleDirectory) + { + DPRINT("ImportModuleDirectory %x\n", ImportModuleDirectory); + + ImportModuleDirectoryCurrent = ImportModuleDirectory; + while (ImportModuleDirectoryCurrent->Name) + { + ImportedName = (PCHAR)Module->DllBase + ImportModuleDirectoryCurrent->Name; + TRACE_LDR("%wZ imports functions from %s\n", &Module->BaseDllName, ImportedName); + + Status = LdrpGetOrLoadModule(SearchPath, ImportedName, &ImportedModule, TRUE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("failed to load %s\n", ImportedName); + return Status; + } + if (Module == ImportedModule) + { + LdrpDecrementLoadCount(Module, FALSE); + } + + TRACE_LDR("Initializing imports for %wZ from %s\n", + &Module->BaseDllName, ImportedName); + Status = LdrpProcessImportDirectoryEntry(Module, ImportedModule, ImportModuleDirectoryCurrent); + if (!NT_SUCCESS(Status)) + { + DPRINT1("failed to import %s\n", ImportedName); + return Status; + } + ImportModuleDirectoryCurrent++; + } + } - while (ImportModuleDirectory->dwRVAModuleName) + if (TlsDirectory && TlsSize > 0) { - PVOID * ImportAddressList; - PULONG FunctionNameList; - UNICODE_STRING DllName; - DWORD pName; - WORD pHint; - - DPRINT("ImportModule->Directory->dwRVAModuleName %s\n", - (PCHAR)(ImageBase + ImportModuleDirectory->dwRVAModuleName)); - - RtlCreateUnicodeStringFromAsciiz (&DllName, - (PCHAR)(ImageBase + ImportModuleDirectory->dwRVAModuleName)); - - Status = LdrGetDllHandle (0, 0, &DllName, &BaseAddress); - if (!NT_SUCCESS(Status)) - { - Status = LdrLoadDll(NULL, - 0, - &DllName, - &BaseAddress); - RtlFreeUnicodeString (&DllName); - if (!NT_SUCCESS(Status)) - { - DbgPrint("LdrFixupImports:failed to load %s\n" - ,(PCHAR)(ImageBase - + ImportModuleDirectory->dwRVAModuleName)); - - return Status; - } - } - - /* - * Get the import address list. - */ - ImportAddressList = (PVOID *)(NTHeaders->OptionalHeader.ImageBase - + ImportModuleDirectory->dwRVAFunctionAddressList); - - /* - * Get the list of functions to import. - */ - if (ImportModuleDirectory->dwRVAFunctionNameList != 0) - { - FunctionNameList = (PULONG) ( - ImageBase - + ImportModuleDirectory->dwRVAFunctionNameList - ); - } - else - { - FunctionNameList = - (PULONG)(ImageBase - + ImportModuleDirectory->dwRVAFunctionAddressList); - } - /* - * Walk through function list and fixup addresses. - */ - while (*FunctionNameList != 0L) - { - if ((*FunctionNameList) & 0x80000000) - { - Ordinal = (*FunctionNameList) & 0x7fffffff; - *ImportAddressList = - LdrGetExportByOrdinal(BaseAddress, - Ordinal); - } - else - { - pName = (DWORD) (ImageBase + *FunctionNameList + 2); - pHint = *(PWORD)(ImageBase + *FunctionNameList); - - *ImportAddressList = - LdrGetExportByName(BaseAddress, (PUCHAR)pName, pHint); - if ((*ImportAddressList) == NULL) - { - DbgPrint("Failed to import %s\n", pName); - return STATUS_UNSUCCESSFUL; - } - } - ImportAddressList++; - FunctionNameList++; - } - ImportModuleDirectory++; + LdrpAcquireTlsSlot(Module, TlsSize, FALSE); } + return STATUS_SUCCESS; } /********************************************************************** * NAME - * LdrPEStartup + * LdrPEStartup * * DESCRIPTION - * 1. Map the DLL's sections into memory. - * 2. Relocate, if needed the DLL. - * 3. Fixup any imported symbol. - * 4. Compute the DLL's entry point. + * 1. Relocate, if needed the EXE. + * 2. Fixup any imported symbol. + * 3. Compute the EXE's entry point. * * ARGUMENTS - * ImageBase - * Address at which the DLL's image - * is loaded. - * - * SectionHandle - * Handle of the section that contains - * the DLL's image. + * ImageBase + * Address at which the EXE's image + * is loaded. + * + * SectionHandle + * Handle of the section that contains + * the EXE's image. * * RETURN VALUE - * NULL on error; otherwise the entry point - * to call for initializing the DLL. + * NULL on error; otherwise the entry point + * to call for initializing the DLL. * * REVISIONS * * NOTE - * + * 04.01.2004 hb Previous this function was used for all images (dll + exe). + * Currently the function is only used for the exe. */ PEPFUNC LdrPEStartup (PVOID ImageBase, - HANDLE SectionHandle) + HANDLE SectionHandle, + PLDR_DATA_TABLE_ENTRY* Module, + PWSTR FullDosName) { - NTSTATUS Status; - PEPFUNC EntryPoint = NULL; - PIMAGE_DOS_HEADER DosHeader; - PIMAGE_NT_HEADERS NTHeaders; + NTSTATUS Status; + PEPFUNC EntryPoint = NULL; + PIMAGE_DOS_HEADER DosHeader; + PIMAGE_NT_HEADERS NTHeaders; + PLDR_DATA_TABLE_ENTRY tmpModule; DPRINT("LdrPEStartup(ImageBase %x SectionHandle %x)\n", ImageBase, (ULONG)SectionHandle); @@ -1030,7 +1881,7 @@ PEPFUNC LdrPEStartup (PVOID ImageBase, * to the DLL's image. */ DosHeader = (PIMAGE_DOS_HEADER) ImageBase; - NTHeaders = (PIMAGE_NT_HEADERS) (ImageBase + DosHeader->e_lfanew); + NTHeaders = (PIMAGE_NT_HEADERS) ((ULONG_PTR)ImageBase + DosHeader->e_lfanew); /* * If the base address is different from the @@ -1039,30 +1890,63 @@ PEPFUNC LdrPEStartup (PVOID ImageBase, */ if (ImageBase != (PVOID) NTHeaders->OptionalHeader.ImageBase) { - DbgPrint("LDR: Performing relocations\n"); + DPRINT("LDR: Performing relocations\n"); Status = LdrPerformRelocations(NTHeaders, ImageBase); if (!NT_SUCCESS(Status)) - { - DbgPrint("LdrPerformRelocations() failed\n"); - return NULL; - } + { + DPRINT1("LdrPerformRelocations() failed\n"); + return NULL; + } + } + + if (Module != NULL) + { + *Module = LdrAddModuleEntry(ImageBase, NTHeaders, FullDosName); + (*Module)->SectionPointer = SectionHandle; + } + else + { + Module = &tmpModule; + Status = LdrFindEntryForAddress(ImageBase, Module); + if (!NT_SUCCESS(Status)) + { + return NULL; + } + } + + if (ImageBase != (PVOID) NTHeaders->OptionalHeader.ImageBase) + { + (*Module)->Flags |= LDRP_IMAGE_NOT_AT_BASE; } /* * If the DLL's imports symbols from other * modules, fixup the imported calls entry points. */ - if (NTHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT] - .VirtualAddress != 0) + DPRINT("About to fixup imports\n"); + Status = LdrFixupImports(NULL, *Module); + if (!NT_SUCCESS(Status)) + { + DPRINT1("LdrFixupImports() failed for %wZ\n", &(*Module)->BaseDllName); + return NULL; + } + DPRINT("Fixup done\n"); + RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); + Status = LdrpInitializeTlsForProccess(); + if (NT_SUCCESS(Status)) + { + Status = LdrpAttachProcess(); + } + if (NT_SUCCESS(Status)) { - DPRINT("About to fixup imports\n"); - Status = LdrFixupImports(NTHeaders, ImageBase); - if (!NT_SUCCESS(Status)) - { - DbgPrint("LdrFixupImports() failed\n"); - return NULL; - } - DPRINT("Fixup done\n"); + LdrpTlsCallback(*Module, DLL_PROCESS_ATTACH); + } + + + RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); + if (!NT_SUCCESS(Status)) + { + return NULL; } /* @@ -1072,589 +1956,1162 @@ PEPFUNC LdrPEStartup (PVOID ImageBase, DPRINT("AddressOfEntryPoint = %x\n",(ULONG)NTHeaders->OptionalHeader.AddressOfEntryPoint); if (NTHeaders->OptionalHeader.AddressOfEntryPoint != 0) { - EntryPoint = (PEPFUNC) (ImageBase - + NTHeaders->OptionalHeader.AddressOfEntryPoint); + EntryPoint = (PEPFUNC) ((ULONG_PTR)ImageBase + + NTHeaders->OptionalHeader.AddressOfEntryPoint); } DPRINT("LdrPEStartup() = %x\n",EntryPoint); return EntryPoint; } +static NTSTATUS +LdrpLoadModule(IN PWSTR SearchPath OPTIONAL, + IN ULONG LoadFlags, + IN PUNICODE_STRING Name, + PLDR_DATA_TABLE_ENTRY *Module, + PVOID *BaseAddress OPTIONAL) +{ + UNICODE_STRING AdjustedName; + UNICODE_STRING FullDosName; + NTSTATUS Status; + PLDR_DATA_TABLE_ENTRY tmpModule; + HANDLE SectionHandle; + ULONG ViewSize; + PVOID ImageBase; + PIMAGE_NT_HEADERS NtHeaders; + BOOLEAN MappedAsDataFile; + + if (Module == NULL) + { + Module = &tmpModule; + } + /* adjust the full dll name */ + LdrAdjustDllName(&AdjustedName, Name, FALSE); + + DPRINT("%wZ\n", &AdjustedName); + + MappedAsDataFile = FALSE; + /* Test if dll is already loaded */ + Status = LdrFindEntryForName(&AdjustedName, Module, TRUE); + if (NT_SUCCESS(Status)) + { + RtlFreeUnicodeString(&AdjustedName); + if (NULL != BaseAddress) + { + *BaseAddress = (*Module)->DllBase; + } + } + else + { + /* Open or create dll image section */ + Status = LdrpMapKnownDll(&AdjustedName, &FullDosName, &SectionHandle); + if (!NT_SUCCESS(Status)) + { + MappedAsDataFile = (0 != (LoadFlags & LOAD_LIBRARY_AS_DATAFILE)); + Status = LdrpMapDllImageFile(SearchPath, &AdjustedName, &FullDosName, + MappedAsDataFile, &SectionHandle); + } + if (!NT_SUCCESS(Status)) + { + DPRINT1("Failed to create or open dll section of '%wZ' (Status %lx)\n", &AdjustedName, Status); + RtlFreeUnicodeString(&AdjustedName); + return Status; + } + RtlFreeUnicodeString(&AdjustedName); + /* Map the dll into the process */ + ViewSize = 0; + ImageBase = 0; + Status = NtMapViewOfSection(SectionHandle, + NtCurrentProcess(), + &ImageBase, + 0, + 0, + NULL, + &ViewSize, + 0, + MEM_COMMIT, + PAGE_READONLY); + if (!NT_SUCCESS(Status)) + { + DPRINT1("map view of section failed (Status %x)\n", Status); + RtlFreeUnicodeString(&FullDosName); + NtClose(SectionHandle); + return(Status); + } + if (NULL != BaseAddress) + { + *BaseAddress = ImageBase; + } + /* Get and check the NT headers */ + NtHeaders = RtlImageNtHeader(ImageBase); + if (NtHeaders == NULL) + { + DPRINT1("RtlImageNtHeaders() failed\n"); + NtUnmapViewOfSection (NtCurrentProcess (), ImageBase); + NtClose (SectionHandle); + RtlFreeUnicodeString(&FullDosName); + return STATUS_UNSUCCESSFUL; + } + DPRINT("Mapped %wZ at %x\n", &FullDosName, ImageBase); + if (MappedAsDataFile) + { + ASSERT(NULL != BaseAddress); + if (NULL != BaseAddress) + { + *BaseAddress = (PVOID) ((char *) *BaseAddress + 1); + } + *Module = NULL; + RtlFreeUnicodeString(&FullDosName); + NtClose(SectionHandle); + return STATUS_SUCCESS; + } + /* If the base address is different from the + * one the DLL is actually loaded, perform any + * relocation. */ + if (ImageBase != (PVOID) NtHeaders->OptionalHeader.ImageBase) + { + DPRINT1("Relocating (%x -> %x) %wZ\n", + NtHeaders->OptionalHeader.ImageBase, ImageBase, &FullDosName); + Status = LdrPerformRelocations(NtHeaders, ImageBase); + if (!NT_SUCCESS(Status)) + { + DPRINT1("LdrPerformRelocations() failed\n"); + NtUnmapViewOfSection (NtCurrentProcess (), ImageBase); + NtClose (SectionHandle); + RtlFreeUnicodeString(&FullDosName); + return STATUS_UNSUCCESSFUL; + } + } + *Module = LdrAddModuleEntry(ImageBase, NtHeaders, FullDosName.Buffer); + (*Module)->SectionPointer = SectionHandle; + if (ImageBase != (PVOID) NtHeaders->OptionalHeader.ImageBase) + { + (*Module)->Flags |= LDRP_IMAGE_NOT_AT_BASE; + } + if (NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) + { + (*Module)->Flags |= LDRP_IMAGE_DLL; + } + /* fixup the imported calls entry points */ + Status = LdrFixupImports(SearchPath, *Module); + if (!NT_SUCCESS(Status)) + { + DPRINT1("LdrFixupImports failed for %wZ, status=%x\n", &(*Module)->BaseDllName, Status); + return Status; + } +#if defined(DBG) || defined(KDBG) + LdrpLoadUserModuleSymbols(*Module); +#endif /* DBG || KDBG */ + RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); + InsertTailList(&NtCurrentPeb()->Ldr->InInitializationOrderModuleList, + &(*Module)->InInitializationOrderModuleList); + RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); + } + return STATUS_SUCCESS; +} -NTSTATUS STDCALL -LdrUnloadDll (IN PVOID BaseAddress) +static NTSTATUS +LdrpUnloadModule(PLDR_DATA_TABLE_ENTRY Module, + BOOLEAN Unload) { - PIMAGE_NT_HEADERS NtHeaders; - PDLLMAIN_FUNC Entrypoint; - PLIST_ENTRY ModuleListHead; - PLIST_ENTRY Entry; - PLDR_MODULE Module; + PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory; + PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundImportDescriptor; + PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundImportDescriptorCurrent; + PCHAR ImportedName; + PLDR_DATA_TABLE_ENTRY ImportedModule; NTSTATUS Status; + LONG LoadCount; + ULONG Size; - if (BaseAddress == NULL) - return STATUS_SUCCESS; + if (Unload) + { + RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); + } - ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; - Entry = ModuleListHead->Flink; + LoadCount = LdrpDecrementLoadCount(Module, Unload); - while (Entry != ModuleListHead); + TRACE_LDR("Unload %wZ, LoadCount %d\n", &Module->BaseDllName, LoadCount); + + if (LoadCount == 0) + { + /* ?????????????????? */ + } + else if (LoadCount == 1) { - Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList); - if (Module->BaseAddress == BaseAddress) - { - if (Module->LoadCount == -1) - { - /* never unload this dll */ - return STATUS_SUCCESS; - } - else if (Module->LoadCount > 1) - { - Module->LoadCount--; - return STATUS_SUCCESS; - } - - NtHeaders = RtlImageNtHeader (Module->BaseAddress); - if ((NtHeaders->FileHeader.Characteristics & IMAGE_FILE_DLL) == IMAGE_FILE_DLL) - { - if (Module->EntryPoint != 0) - { - Entrypoint = (PDLLMAIN_FUNC)Module->EntryPoint; - DPRINT("Calling entry point at 0x%08x\n", Entrypoint); - Entrypoint(Module->BaseAddress, - DLL_PROCESS_DETACH, - NULL); - } - else - { - DPRINT("NTDLL.LDR: Entrypoint is NULL for \n"); - } - } - Status = ZwUnmapViewOfSection (NtCurrentProcess (), - Module->BaseAddress); - ZwClose (Module->SectionHandle); - - /* remove the module entry from the list */ - RtlFreeUnicodeString (&Module->FullDllName); - RtlFreeUnicodeString (&Module->BaseDllName); - RemoveEntryList (Entry); - RtlFreeHeap (RtlGetProcessHeap (), 0, Module); - - return Status; - } - - Entry = Entry->Flink; + BoundImportDescriptor = (PIMAGE_BOUND_IMPORT_DESCRIPTOR) + RtlImageDirectoryEntryToData(Module->DllBase, + TRUE, + IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT, + &Size); + if (BoundImportDescriptor) + { + /* dereferencing all imported modules, use the bound import descriptor */ + BoundImportDescriptorCurrent = BoundImportDescriptor; + while (BoundImportDescriptorCurrent->OffsetModuleName) + { + ImportedName = (PCHAR)BoundImportDescriptor + BoundImportDescriptorCurrent->OffsetModuleName; + TRACE_LDR("%wZ trys to unload %s\n", &Module->BaseDllName, ImportedName); + Status = LdrpGetOrLoadModule(NULL, ImportedName, &ImportedModule, FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("unable to found imported modul %s\n", ImportedName); + } + else + { + if (Module != ImportedModule) + { + Status = LdrpUnloadModule(ImportedModule, FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("unable to unload %s\n", ImportedName); + } + } + } + BoundImportDescriptorCurrent++; + } + } + else + { + ImportModuleDirectory = (PIMAGE_IMPORT_DESCRIPTOR) + RtlImageDirectoryEntryToData(Module->DllBase, + TRUE, + IMAGE_DIRECTORY_ENTRY_IMPORT, + &Size); + if (ImportModuleDirectory) + { + /* dereferencing all imported modules, use the import descriptor */ + while (ImportModuleDirectory->Name) + { + ImportedName = (PCHAR)Module->DllBase + ImportModuleDirectory->Name; + TRACE_LDR("%wZ trys to unload %s\n", &Module->BaseDllName, ImportedName); + Status = LdrpGetOrLoadModule(NULL, ImportedName, &ImportedModule, FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("unable to found imported modul %s\n", ImportedName); + } + else + { + if (Module != ImportedModule) + { + Status = LdrpUnloadModule(ImportedModule, FALSE); + if (!NT_SUCCESS(Status)) + { + DPRINT1("unable to unload %s\n", ImportedName); + } + } + } + ImportModuleDirectory++; + } + } + } } - DPRINT("NTDLL.LDR: Dll not found\n") + if (Unload) + { + LdrpDetachProcess(FALSE); + RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); + } + return STATUS_SUCCESS; - return STATUS_UNSUCCESSFUL; } - -NTSTATUS STDCALL -LdrFindResource_U(PVOID BaseAddress, - PLDR_RESOURCE_INFO ResourceInfo, - ULONG Level, - PIMAGE_RESOURCE_DATA_ENTRY *ResourceDataEntry) +/* + * @implemented + */ +NTSTATUS NTAPI +LdrUnloadDll (IN PVOID BaseAddress) { - PIMAGE_RESOURCE_DIRECTORY ResDir; - PIMAGE_RESOURCE_DIRECTORY ResBase; - PIMAGE_RESOURCE_DIRECTORY_ENTRY ResEntry; - NTSTATUS Status = STATUS_SUCCESS; - ULONG EntryCount; - PWCHAR ws; - ULONG i; - ULONG Id; + PLDR_DATA_TABLE_ENTRY Module; + NTSTATUS Status; - DPRINT ("LdrFindResource_U()\n"); + if (BaseAddress == NULL) + return STATUS_SUCCESS; - /* Get the pointer to the resource directory */ - ResDir = (PIMAGE_RESOURCE_DIRECTORY) - RtlImageDirectoryEntryToData (BaseAddress, - TRUE, - IMAGE_DIRECTORY_ENTRY_RESOURCE, - &i); - if (ResDir == NULL) + if (LdrMappedAsDataFile(&BaseAddress)) { - return STATUS_RESOURCE_DATA_NOT_FOUND; + Status = NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress); } - - DPRINT("ResourceDirectory: %x\n", (ULONG)ResDir); - - ResBase = ResDir; - - /* Let's go into resource tree */ - for (i = 0; i < Level; i++) + else { - DPRINT("ResDir: %x\n", (ULONG)ResDir); - Id = ((PULONG)ResourceInfo)[i]; - EntryCount = ResDir->NumberOfNamedEntries; - ResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResDir + 1); - DPRINT("ResEntry %x\n", (ULONG)ResEntry); - if (Id & 0xFFFF0000) - { - /* Resource name is a unicode string */ - for (; EntryCount--; ResEntry++) - { - /* Scan entries for equal name */ - if (ResEntry->Name & 0x80000000) - { - ws = (PWCHAR)((ULONG)ResDir + (ResEntry->Name & 0x7FFFFFFF)); - if (!wcsncmp((PWCHAR)Id, ws + 1, *ws ) && - wcslen((PWCHAR)Id) == (int)*ws ) - { - goto found; - } - } - } - } - else - { - /* We use ID number instead of string */ - ResEntry += EntryCount; - EntryCount = ResDir->NumberOfIdEntries; - for (; EntryCount--; ResEntry++) - { - /* Scan entries for equal name */ - if (ResEntry->Name == Id) - { - DPRINT("ID entry found %x\n", Id); - goto found; - } - } - } - DPRINT("Error %lu\n", i); - - switch (i) - { - case 0: - return STATUS_RESOURCE_TYPE_NOT_FOUND; - - case 1: - return STATUS_RESOURCE_NAME_NOT_FOUND; - - case 2: - if (ResDir->NumberOfNamedEntries || ResDir->NumberOfIdEntries) - { - /* Use the first available language */ - ResEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(ResDir + 1); - break; - } - return STATUS_RESOURCE_LANG_NOT_FOUND; - - case 3: - return STATUS_RESOURCE_DATA_NOT_FOUND; - - default: - return STATUS_INVALID_PARAMETER; - } -found:; - ResDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG)ResBase + - (ResEntry->OffsetToData & 0x7FFFFFFF)); + Status = LdrFindEntryForAddress(BaseAddress, &Module); + if (NT_SUCCESS(Status)) + { + TRACE_LDR("LdrUnloadDll, , unloading %wZ\n", &Module->BaseDllName); + Status = LdrpUnloadModule(Module, TRUE); + } } - DPRINT("ResourceDataEntry: %x\n", (ULONG)ResDir); - if (ResourceDataEntry) - { - *ResourceDataEntry = (PVOID)ResDir; - } + return Status; +} - return Status; +/* + * @implemented + */ +NTSTATUS NTAPI +LdrDisableThreadCalloutsForDll(IN PVOID BaseAddress) +{ + PLIST_ENTRY ModuleListHead; + PLIST_ENTRY Entry; + PLDR_DATA_TABLE_ENTRY Module; + NTSTATUS Status; + + DPRINT("LdrDisableThreadCalloutsForDll (BaseAddress %x)\n", BaseAddress); + + Status = STATUS_DLL_NOT_FOUND; + RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); + ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; + Entry = ModuleListHead->Flink; + while (Entry != ModuleListHead) + { + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList); + + DPRINT("BaseDllName %wZ BaseAddress %x\n", &Module->BaseDllName, Module->DllBase); + + if (Module->DllBase == BaseAddress) + { + if (Module->TlsIndex == 0xFFFF) + { + Module->Flags |= LDRP_DONT_CALL_FOR_THREADS; + Status = STATUS_SUCCESS; + } + break; + } + Entry = Entry->Flink; + } + RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); + return Status; } +/* + * @implemented + */ +NTSTATUS NTAPI +LdrGetDllHandle(IN PWSTR DllPath OPTIONAL, + IN PULONG DllCharacteristics, + IN PUNICODE_STRING DllName, + OUT PVOID *DllHandle) +{ + PLDR_DATA_TABLE_ENTRY Module; + NTSTATUS Status; + + TRACE_LDR("LdrGetDllHandle, searching for %wZ from %S\n", + DllName, DllPath ? DllPath : L""); + + /* NULL is the current executable */ + if (DllName == NULL) + { + *DllHandle = ExeModule->DllBase; + DPRINT("BaseAddress %x\n", *DllHandle); + return STATUS_SUCCESS; + } + + Status = LdrFindEntryForName(DllName, &Module, FALSE); + if (NT_SUCCESS(Status)) + { + *DllHandle = Module->DllBase; + return STATUS_SUCCESS; + } + + DPRINT("Failed to find dll %wZ\n", DllName); + *DllHandle = NULL; + return STATUS_DLL_NOT_FOUND; +} -NTSTATUS STDCALL -LdrAccessResource(IN PVOID BaseAddress, - IN PIMAGE_RESOURCE_DATA_ENTRY ResourceDataEntry, - OUT PVOID *Resource OPTIONAL, - OUT PULONG Size OPTIONAL) + +/* + * @implemented + */ +NTSTATUS NTAPI +LdrGetProcedureAddress (IN PVOID BaseAddress, + IN PANSI_STRING Name, + IN ULONG Ordinal, + OUT PVOID *ProcedureAddress) { - PIMAGE_SECTION_HEADER Section; - PIMAGE_NT_HEADERS NtHeader; - ULONG SectionRva; - ULONG SectionVa; - ULONG DataSize; - ULONG Offset = 0; - ULONG Data; - - Data = (ULONG)RtlImageDirectoryEntryToData (BaseAddress, - TRUE, - IMAGE_DIRECTORY_ENTRY_RESOURCE, - &DataSize); - if (Data == 0) - return STATUS_RESOURCE_DATA_NOT_FOUND; - - if ((ULONG)BaseAddress & 1) + if (Name && Name->Length) { - /* loaded as ordinary file */ - NtHeader = RtlImageNtHeader((PVOID)((ULONG)BaseAddress & ~1UL)); - Offset = (ULONG)BaseAddress - Data + NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress; - Section = RtlImageRvaToSection (NtHeader, BaseAddress, NtHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress); - if (Section == NULL) - { - return STATUS_RESOURCE_DATA_NOT_FOUND; - } - - if (Section->Misc.VirtualSize < ResourceDataEntry->OffsetToData) - { - SectionRva = RtlImageRvaToSection (NtHeader, BaseAddress, ResourceDataEntry->OffsetToData)->VirtualAddress; - SectionVa = RtlImageRvaToVa(NtHeader, BaseAddress, SectionRva, NULL); - Offset = SectionRva - SectionVa + Data - Section->VirtualAddress; - } + TRACE_LDR("LdrGetProcedureAddress by NAME - %Z\n", Name); + } + else + { + TRACE_LDR("LdrGetProcedureAddress by ORDINAL - %d\n", Ordinal); } - if (Resource) + DPRINT("LdrGetProcedureAddress (BaseAddress %x Name %Z Ordinal %lu ProcedureAddress %x)\n", + BaseAddress, Name, Ordinal, ProcedureAddress); + + if (Name && Name->Length) { - *Resource = (PVOID)(ResourceDataEntry->OffsetToData - Offset + (ULONG)BaseAddress); + /* by name */ + *ProcedureAddress = LdrGetExportByName(BaseAddress, (PUCHAR)Name->Buffer, 0xffff); + if (*ProcedureAddress != NULL) + { + return STATUS_SUCCESS; + } + DPRINT("LdrGetProcedureAddress: Can't resolve symbol '%Z'\n", Name); } + else + { + /* by ordinal */ + Ordinal &= 0x0000FFFF; + *ProcedureAddress = LdrGetExportByOrdinal(BaseAddress, (WORD)Ordinal); + if (*ProcedureAddress) + { + return STATUS_SUCCESS; + } + DPRINT("LdrGetProcedureAddress: Can't resolve symbol @%d\n", Ordinal); + } + return STATUS_PROCEDURE_NOT_FOUND; +} + +/********************************************************************** + * NAME LOCAL + * LdrpDetachProcess + * + * DESCRIPTION + * Unload dll's which are no longer referenced from others dll's + * + * ARGUMENTS + * none + * + * RETURN VALUE + * none + * + * REVISIONS + * + * NOTE + * The loader lock must be held on enty. + */ +static VOID +LdrpDetachProcess(BOOLEAN UnloadAll) +{ + PLIST_ENTRY ModuleListHead; + PLIST_ENTRY Entry; + PLDR_DATA_TABLE_ENTRY Module; + static ULONG CallingCount = 0; + + DPRINT("LdrpDetachProcess() called for %wZ\n", + &ExeModule->BaseDllName); + + CallingCount++; - if (Size) + ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; + Entry = ModuleListHead->Blink; + while (Entry != ModuleListHead) { - *Size = ResourceDataEntry->Size; + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList); + if (((UnloadAll && Module->LoadCount == 0xFFFF) || Module->LoadCount == 0) && + Module->Flags & LDRP_ENTRY_PROCESSED && + !(Module->Flags & LDRP_UNLOAD_IN_PROGRESS)) + { + Module->Flags |= LDRP_UNLOAD_IN_PROGRESS; + if (Module == LdrpLastModule) + { + LdrpLastModule = NULL; + } + if (Module->Flags & LDRP_PROCESS_ATTACH_CALLED) + { + TRACE_LDR("Unload %wZ - Calling entry point at %x\n", + &Module->BaseDllName, Module->EntryPoint); + LdrpCallDllEntry(Module, DLL_PROCESS_DETACH, (PVOID)(Module->LoadCount == 0xFFFF ? 1 : 0)); + } + else + { + TRACE_LDR("Unload %wZ\n", &Module->BaseDllName); + } + Entry = ModuleListHead->Blink; + } + else + { + Entry = Entry->Blink; + } } - return STATUS_SUCCESS; + if (CallingCount == 1) + { + Entry = ModuleListHead->Blink; + while (Entry != ModuleListHead) + { + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList); + Entry = Entry->Blink; + if (Module->Flags & LDRP_UNLOAD_IN_PROGRESS && + ((UnloadAll && Module->LoadCount != 0xFFFF) || Module->LoadCount == 0)) + { + /* remove the module entry from the list */ + RemoveEntryList (&Module->InLoadOrderModuleList); + RemoveEntryList (&Module->InInitializationOrderModuleList); + + NtUnmapViewOfSection (NtCurrentProcess (), Module->DllBase); + NtClose (Module->SectionPointer); + + TRACE_LDR("%wZ unloaded\n", &Module->BaseDllName); + + RtlFreeUnicodeString (&Module->FullDllName); + RtlFreeUnicodeString (&Module->BaseDllName); + + RtlFreeHeap (RtlGetProcessHeap (), 0, Module); + } + } + } + CallingCount--; + DPRINT("LdrpDetachProcess() done\n"); } - -NTSTATUS STDCALL -LdrDisableThreadCalloutsForDll (IN PVOID BaseAddress) +/********************************************************************** + * NAME LOCAL + * LdrpAttachProcess + * + * DESCRIPTION + * Initialize all dll's which are prepered for loading + * + * ARGUMENTS + * none + * + * RETURN VALUE + * status + * + * REVISIONS + * + * NOTE + * The loader lock must be held on entry. + * + */ +static NTSTATUS +LdrpAttachProcess(VOID) { PLIST_ENTRY ModuleListHead; PLIST_ENTRY Entry; - PLDR_MODULE Module; - NTSTATUS Status; - - DPRINT("LdrDisableThreadCalloutsForDll (BaseAddress %x)\n", - BaseAddress); + PLDR_DATA_TABLE_ENTRY Module; + BOOLEAN Result; + NTSTATUS Status = STATUS_SUCCESS; - Status = STATUS_DLL_NOT_FOUND; + DPRINT("LdrpAttachProcess() called for %wZ\n", + &ExeModule->BaseDllName); - ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; + ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; Entry = ModuleListHead->Flink; - while (Entry != ModuleListHead) { - Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList); - - DPRINT("BaseDllName %wZ BaseAddress %x\n", - &Module->BaseDllName, - Module->BaseAddress); - - if (Module->BaseAddress == BaseAddress) - { - if (Module->TlsIndex == 0) - { - Module->Flags |= 0x00040000; - Status = STATUS_SUCCESS; - } - return Status; - } - - Entry = Entry->Flink; + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList); + if (!(Module->Flags & (LDRP_LOAD_IN_PROGRESS|LDRP_UNLOAD_IN_PROGRESS|LDRP_ENTRY_PROCESSED))) + { + Module->Flags |= LDRP_LOAD_IN_PROGRESS; + TRACE_LDR("%wZ loaded - Calling init routine at %x for process attaching\n", + &Module->BaseDllName, Module->EntryPoint); + Result = LdrpCallDllEntry(Module, DLL_PROCESS_ATTACH, (PVOID)(Module->LoadCount == 0xFFFF ? 1 : 0)); + if (!Result) + { + Status = STATUS_DLL_INIT_FAILED; + break; + } + if (Module->Flags & LDRP_IMAGE_DLL && Module->EntryPoint != 0) + { + Module->Flags |= LDRP_PROCESS_ATTACH_CALLED|LDRP_ENTRY_PROCESSED; + } + else + { + Module->Flags |= LDRP_ENTRY_PROCESSED; + } + Module->Flags &= ~LDRP_LOAD_IN_PROGRESS; + } + Entry = Entry->Flink; } + DPRINT("LdrpAttachProcess() done\n"); + return Status; } +/* + * @implemented + */ +NTSTATUS NTAPI +LdrShutdownProcess (VOID) +{ + LdrpDetachProcess(TRUE); + return STATUS_SUCCESS; +} + +/* + * @implemented + */ -NTSTATUS STDCALL -LdrFindResourceDirectory_U (IN PVOID BaseAddress, - WCHAR **name, - DWORD level, - OUT PVOID *addr) +NTSTATUS +LdrpAttachThread (VOID) { - PIMAGE_RESOURCE_DIRECTORY ResDir; - PIMAGE_RESOURCE_DIRECTORY_ENTRY ResEntry; - ULONG EntryCount; - ULONG i; - NTSTATUS Status = STATUS_SUCCESS; - WCHAR *ws; - - /* Get the pointer to the resource directory */ - ResDir = (PIMAGE_RESOURCE_DIRECTORY) - RtlImageDirectoryEntryToData (BaseAddress, - TRUE, - IMAGE_DIRECTORY_ENTRY_RESOURCE, - &i); - if (ResDir == NULL) - { - return STATUS_RESOURCE_DATA_NOT_FOUND; - } + PLIST_ENTRY ModuleListHead; + PLIST_ENTRY Entry; + PLDR_DATA_TABLE_ENTRY Module; + NTSTATUS Status; - /* Let's go into resource tree */ - for (i = 0; i < level; i++, name++) - { - EntryCount = ResDir->NumberOfNamedEntries; - ResEntry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)(ResDir + 1); - if ((ULONG)(*name) & 0xFFFF0000) - { - /* Resource name is a unicode string */ - for (; EntryCount--; ResEntry++) - { - /* Scan entries for equal name */ - if (ResEntry->Name & 0x80000000) - { - ws = (WCHAR*)((ULONG)ResDir + (ResEntry->Name & 0x7FFFFFFF)); - if (!wcsncmp( *name, ws + 1, *ws ) && wcslen( *name ) == (int)*ws ) - { - goto found; - } - } - } - } - else - { - /* We use ID number instead of string */ - ResEntry += EntryCount; - EntryCount = ResDir->NumberOfIdEntries; - for (; EntryCount--; ResEntry++) - { - /* Scan entries for equal name */ - if (ResEntry->Name == (ULONG)(*name)) - goto found; - } - } - - switch (i) - { - case 0: - return STATUS_RESOURCE_TYPE_NOT_FOUND; - - case 1: - return STATUS_RESOURCE_NAME_NOT_FOUND; - - case 2: - Status = STATUS_RESOURCE_LANG_NOT_FOUND; - /* Just use first language entry */ - if (ResDir->NumberOfNamedEntries || ResDir->NumberOfIdEntries) - { - ResEntry = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(ResDir + 1); - break; - } - return Status; - - case 3: - return STATUS_RESOURCE_DATA_NOT_FOUND; - - default: - return STATUS_INVALID_PARAMETER; - } -found:; - ResDir = (PIMAGE_RESOURCE_DIRECTORY)((ULONG)ResDir + ResEntry->OffsetToData); - } + DPRINT("LdrpAttachThread() called for %wZ\n", + &ExeModule->BaseDllName); - if (addr) - { - *addr = (PVOID)ResDir; - } + RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); + + Status = LdrpInitializeTlsForThread(); + + if (NT_SUCCESS(Status)) + { + ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; + Entry = ModuleListHead->Flink; + + while (Entry != ModuleListHead) + { + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList); + if (Module->Flags & LDRP_PROCESS_ATTACH_CALLED && + !(Module->Flags & LDRP_DONT_CALL_FOR_THREADS) && + !(Module->Flags & LDRP_UNLOAD_IN_PROGRESS)) + { + TRACE_LDR("%wZ - Calling entry point at %x for thread attaching\n", + &Module->BaseDllName, Module->EntryPoint); + LdrpCallDllEntry(Module, DLL_THREAD_ATTACH, NULL); + } + Entry = Entry->Flink; + } + + Entry = NtCurrentPeb()->Ldr->InLoadOrderModuleList.Flink; + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList); + LdrpTlsCallback(Module, DLL_THREAD_ATTACH); + } + + RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); + + DPRINT("LdrpAttachThread() done\n"); return Status; } -NTSTATUS STDCALL -LdrGetDllHandle (IN ULONG Unknown1, - IN ULONG Unknown2, - IN PUNICODE_STRING DllName, - OUT PVOID *BaseAddress) +/* + * @implemented + */ +NTSTATUS NTAPI +LdrShutdownThread (VOID) { - UNICODE_STRING FullDllName; PLIST_ENTRY ModuleListHead; PLIST_ENTRY Entry; - PLDR_MODULE Module; + PLDR_DATA_TABLE_ENTRY Module; - DPRINT("LdrGetDllHandle (Unknown1 %x Unknown2 %x DllName %wZ BaseAddress %p)\n", - Unknown1, Unknown2, DllName, BaseAddress); + DPRINT("LdrShutdownThread() called for %wZ\n", + &ExeModule->BaseDllName); - /* NULL is the current executable */ - if ( DllName == NULL ) + RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); + + ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; + Entry = ModuleListHead->Blink; + while (Entry != ModuleListHead) { - *BaseAddress = NtCurrentPeb()->ImageBaseAddress; - DPRINT("BaseAddress %x\n", *BaseAddress); - return STATUS_SUCCESS; + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList); + + if (Module->Flags & LDRP_PROCESS_ATTACH_CALLED && + !(Module->Flags & LDRP_DONT_CALL_FOR_THREADS) && + !(Module->Flags & LDRP_UNLOAD_IN_PROGRESS)) + { + TRACE_LDR("%wZ - Calling entry point at %x for thread detaching\n", + &Module->BaseDllName, Module->EntryPoint); + LdrpCallDllEntry(Module, DLL_THREAD_DETACH, NULL); + } + Entry = Entry->Blink; } - LdrAdjustDllName (&FullDllName, - DllName, - TRUE); - - DPRINT("FullDllName %wZ\n", - &FullDllName); - - ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; - Entry = ModuleListHead->Flink; + RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); - while (Entry != ModuleListHead) + if (LdrpTlsArray) { - Module = CONTAINING_RECORD(Entry, LDR_MODULE, InLoadOrderModuleList); - - DPRINT("EntryPoint %x\n", Module->EntryPoint); - DPRINT("Comparing %wZ and %wZ\n", - &Module->BaseDllName, - &FullDllName); - - if (!RtlCompareUnicodeString(&Module->BaseDllName, &FullDllName, TRUE)) - { - RtlFreeUnicodeString (&FullDllName); - *BaseAddress = Module->BaseAddress; - DPRINT("BaseAddress %x\n", *BaseAddress); - return STATUS_SUCCESS; - } - - Entry = Entry->Flink; + RtlFreeHeap (RtlGetProcessHeap(), 0, NtCurrentTeb()->ThreadLocalStoragePointer); } - DPRINT("Failed to find dll %wZ\n", &FullDllName); - RtlFreeUnicodeString (&FullDllName); - *BaseAddress = NULL; - return STATUS_DLL_NOT_FOUND; + DPRINT("LdrShutdownThread() done\n"); + + return STATUS_SUCCESS; } -NTSTATUS STDCALL -LdrGetProcedureAddress (IN PVOID BaseAddress, - IN PANSI_STRING Name, - IN ULONG Ordinal, - OUT PVOID *ProcedureAddress) +/*************************************************************************** + * NAME EXPORTED + * LdrQueryProcessModuleInformation + * + * DESCRIPTION + * + * ARGUMENTS + * + * RETURN VALUE + * + * REVISIONS + * + * NOTE + * + * @implemented + */ +NTSTATUS NTAPI +LdrQueryProcessModuleInformation(IN PRTL_PROCESS_MODULES ModuleInformation OPTIONAL, + IN ULONG Size OPTIONAL, + OUT PULONG ReturnedSize) { - PIMAGE_EXPORT_DIRECTORY ExportDir; - PUSHORT OrdinalPtr; - PULONG NamePtr; - PULONG AddressPtr; - ULONG i = 0; + PLIST_ENTRY ModuleListHead; + PLIST_ENTRY Entry; + PLDR_DATA_TABLE_ENTRY Module; + PRTL_PROCESS_MODULE_INFORMATION ModulePtr = NULL; + NTSTATUS Status = STATUS_SUCCESS; + ULONG UsedSize = sizeof(ULONG); + ANSI_STRING AnsiString; + PCHAR p; - DPRINT("LdrGetProcedureAddress (BaseAddress %x Name %Z Ordinal %lu ProcedureAddress %x)\n", - BaseAddress, Name, Ordinal, ProcedureAddress); + DPRINT("LdrQueryProcessModuleInformation() called\n"); - /* Get the pointer to the export directory */ - ExportDir = (PIMAGE_EXPORT_DIRECTORY) - RtlImageDirectoryEntryToData (BaseAddress, - TRUE, - IMAGE_DIRECTORY_ENTRY_EXPORT, - &i); + RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); - DPRINT("ExportDir %x i %lu\n", ExportDir, i); + if (ModuleInformation == NULL || Size == 0) + { + Status = STATUS_INFO_LENGTH_MISMATCH; + } + else + { + ModuleInformation->ModuleCount = 0; + ModulePtr = &ModuleInformation->ModuleEntry[0]; + Status = STATUS_SUCCESS; + } - if (!ExportDir || !i || !ProcedureAddress) - { - return STATUS_INVALID_PARAMETER; - } + ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; + Entry = ModuleListHead->Flink; - AddressPtr = (PULONG)((ULONG)BaseAddress + (ULONG)ExportDir->AddressOfFunctions); - if (Name && Name->Length) + while (Entry != ModuleListHead) + { + Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList); + + DPRINT(" Module %wZ\n", + &Module->FullDllName); + + if (UsedSize > Size) + { + Status = STATUS_INFO_LENGTH_MISMATCH; + } + else if (ModuleInformation != NULL) + { + ModulePtr->Reserved[0] = ModulePtr->Reserved[1] = 0; // FIXME: ?? + ModulePtr->Base = Module->DllBase; + ModulePtr->Size = Module->SizeOfImage; + ModulePtr->Flags = Module->Flags; + ModulePtr->Index = 0; // FIXME: index ?? + ModulePtr->Unknown = 0; // FIXME: ?? + ModulePtr->LoadCount = Module->LoadCount; + + AnsiString.Length = 0; + AnsiString.MaximumLength = 256; + AnsiString.Buffer = ModulePtr->ImageName; + RtlUnicodeStringToAnsiString(&AnsiString, + &Module->FullDllName, + FALSE); + p = strrchr(ModulePtr->ImageName, '\\'); + if (p != NULL) + ModulePtr->ModuleNameOffset = p - ModulePtr->ImageName + 1; + else + ModulePtr->ModuleNameOffset = 0; + + ModulePtr++; + ModuleInformation->ModuleCount++; + } + UsedSize += sizeof(RTL_PROCESS_MODULE_INFORMATION); + + Entry = Entry->Flink; + } + + RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); + + if (ReturnedSize != 0) + *ReturnedSize = UsedSize; + + DPRINT("LdrQueryProcessModuleInformation() done\n"); + + return(Status); +} + + +static BOOLEAN +LdrpCheckImageChecksum (IN PVOID BaseAddress, + IN ULONG ImageSize) +{ + PIMAGE_NT_HEADERS Header; + PUSHORT Ptr; + ULONG Sum; + ULONG CalcSum; + ULONG HeaderSum; + ULONG i; + + Header = RtlImageNtHeader (BaseAddress); + if (Header == NULL) + return FALSE; + + HeaderSum = Header->OptionalHeader.CheckSum; + if (HeaderSum == 0) + return TRUE; + + Sum = 0; + Ptr = (PUSHORT) BaseAddress; + for (i = 0; i < ImageSize / sizeof (USHORT); i++) { - /* by name */ - OrdinalPtr = (PUSHORT)((ULONG)BaseAddress + (ULONG)ExportDir->AddressOfNameOrdinals); - NamePtr = (PULONG)((ULONG)BaseAddress + (ULONG)ExportDir->AddressOfNames); - for( i = 0; i < ExportDir->NumberOfNames; i++, NamePtr++, OrdinalPtr++) - { - if (!_strnicmp(Name->Buffer, (char*)(BaseAddress + *NamePtr), Name->Length)) - { - *ProcedureAddress = (PVOID)((ULONG)BaseAddress + (ULONG)AddressPtr[*OrdinalPtr]); - return STATUS_SUCCESS; - } - } - DbgPrint("LdrGetProcedureAddress: Can't resolve symbol '%Z'\n", Name); + Sum += (ULONG)*Ptr; + if (HIWORD(Sum) != 0) + { + Sum = LOWORD(Sum) + HIWORD(Sum); + } + Ptr++; } - else - { - /* by ordinal */ - Ordinal &= 0x0000FFFF; - if (Ordinal - ExportDir->Base < ExportDir->NumberOfFunctions) - { - *ProcedureAddress = (PVOID)((ULONG)BaseAddress + (ULONG)AddressPtr[Ordinal - ExportDir->Base]); - return STATUS_SUCCESS; - } - DbgPrint("LdrGetProcedureAddress: Can't resolve symbol @%d\n", Ordinal); - } - return STATUS_PROCEDURE_NOT_FOUND; -} + if (ImageSize & 1) + { + Sum += (ULONG)*((PUCHAR)Ptr); + if (HIWORD(Sum) != 0) + { + Sum = LOWORD(Sum) + HIWORD(Sum); + } + } + CalcSum = (USHORT)(LOWORD(Sum) + HIWORD(Sum)); -NTSTATUS STDCALL -LdrShutdownProcess (VOID) + /* Subtract image checksum from calculated checksum. */ + /* fix low word of checksum */ + if (LOWORD(CalcSum) >= LOWORD(HeaderSum)) + { + CalcSum -= LOWORD(HeaderSum); + } + else + { + CalcSum = ((LOWORD(CalcSum) - LOWORD(HeaderSum)) & 0xFFFF) - 1; + } + + /* fix high word of checksum */ + if (LOWORD(CalcSum) >= HIWORD(HeaderSum)) + { + CalcSum -= HIWORD(HeaderSum); + } + else + { + CalcSum = ((LOWORD(CalcSum) - HIWORD(HeaderSum)) & 0xFFFF) - 1; + } + + /* add file length */ + CalcSum += ImageSize; + + return (BOOLEAN)(CalcSum == HeaderSum); +} + +/* + * Compute size of an image as it is actually present in virt memory + * (i.e. excluding NEVER_LOAD sections) + */ +ULONG +LdrpGetResidentSize(PIMAGE_NT_HEADERS NTHeaders) { - PLIST_ENTRY ModuleListHead; - PLIST_ENTRY Entry; - PLDR_MODULE Module; + PIMAGE_SECTION_HEADER SectionHeader; + unsigned SectionIndex; + ULONG ResidentSize; + + SectionHeader = (PIMAGE_SECTION_HEADER)((char *) &NTHeaders->OptionalHeader + + NTHeaders->FileHeader.SizeOfOptionalHeader); + ResidentSize = 0; + for (SectionIndex = 0; SectionIndex < NTHeaders->FileHeader.NumberOfSections; SectionIndex++) + { + if (0 == (SectionHeader->Characteristics & IMAGE_SCN_LNK_REMOVE) + && ResidentSize < SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize) + { + ResidentSize = SectionHeader->VirtualAddress + SectionHeader->Misc.VirtualSize; + } + SectionHeader++; + } - DPRINT("LdrShutdownProcess() called\n"); + return ResidentSize; +} - RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); - ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; - Entry = ModuleListHead->Blink; +/*************************************************************************** + * NAME EXPORTED + * LdrVerifyImageMatchesChecksum + * + * DESCRIPTION + * + * ARGUMENTS + * + * RETURN VALUE + * + * REVISIONS + * + * NOTE + * + * @implemented + */ +NTSTATUS NTAPI +LdrVerifyImageMatchesChecksum (IN HANDLE FileHandle, + ULONG Unknown1, + ULONG Unknown2, + ULONG Unknown3) +{ + FILE_STANDARD_INFORMATION FileInfo; + IO_STATUS_BLOCK IoStatusBlock; + HANDLE SectionHandle; + ULONG ViewSize; + PVOID BaseAddress; + BOOLEAN Result; + NTSTATUS Status; + + DPRINT ("LdrVerifyImageMatchesChecksum() called\n"); + + Status = NtCreateSection (&SectionHandle, + SECTION_MAP_READ, + NULL, + NULL, + PAGE_READONLY, + SEC_COMMIT, + FileHandle); + if (!NT_SUCCESS(Status)) + { + DPRINT1 ("NtCreateSection() failed (Status %lx)\n", Status); + return Status; + } - while (Entry != ModuleListHead) - { - Module = CONTAINING_RECORD(Entry, LDR_MODULE, InInitializationOrderModuleList); - - DPRINT(" Unloading %S\n", - &Module->BaseDllName); - // PJS: only detach from static dlls, they should FreeLibrary() any dlls that - // they loaded dynamically, and when the last reference is gone, that lib will - // be detached. - if (Module->EntryPoint != 0 && Module->LoadCount == -1) - { - PDLLMAIN_FUNC Entrypoint = (PDLLMAIN_FUNC)Module->EntryPoint; - - DPRINT("Calling entry point at 0x%08x\n", Entrypoint); - Entrypoint (Module->BaseAddress, - DLL_PROCESS_DETACH, - NULL); - } - - Entry = Entry->Blink; - } + ViewSize = 0; + BaseAddress = NULL; + Status = NtMapViewOfSection (SectionHandle, + NtCurrentProcess (), + &BaseAddress, + 0, + 0, + NULL, + &ViewSize, + ViewShare, + 0, + PAGE_READONLY); + if (!NT_SUCCESS(Status)) + { + DPRINT1 ("NtMapViewOfSection() failed (Status %lx)\n", Status); + NtClose (SectionHandle); + return Status; + } - RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); + Status = NtQueryInformationFile (FileHandle, + &IoStatusBlock, + &FileInfo, + sizeof (FILE_STANDARD_INFORMATION), + FileStandardInformation); + if (!NT_SUCCESS(Status)) + { + DPRINT1 ("NtMapViewOfSection() failed (Status %lx)\n", Status); + NtUnmapViewOfSection (NtCurrentProcess (), + BaseAddress); + NtClose (SectionHandle); + return Status; + } + + Result = LdrpCheckImageChecksum (BaseAddress, + FileInfo.EndOfFile.u.LowPart); + if (Result == FALSE) + { + Status = STATUS_IMAGE_CHECKSUM_MISMATCH; + } - DPRINT("LdrShutdownProcess() done\n"); + NtUnmapViewOfSection (NtCurrentProcess (), + BaseAddress); - return STATUS_SUCCESS; + NtClose (SectionHandle); + + return Status; } -NTSTATUS STDCALL -LdrShutdownThread (VOID) +/*************************************************************************** + * NAME EXPORTED + * LdrQueryImageFileExecutionOptions + * + * DESCRIPTION + * + * ARGUMENTS + * + * RETURN VALUE + * + * REVISIONS + * + * NOTE + * + * @implemented + */ +NTSTATUS NTAPI +LdrQueryImageFileExecutionOptions (IN PUNICODE_STRING SubKey, + IN PCWSTR ValueName, + IN ULONG Type, + OUT PVOID Buffer, + IN ULONG BufferSize, + OUT PULONG ReturnedLength OPTIONAL) { - PLIST_ENTRY ModuleListHead; - PLIST_ENTRY Entry; - PLDR_MODULE Module; + PKEY_VALUE_PARTIAL_INFORMATION KeyInfo; + OBJECT_ATTRIBUTES ObjectAttributes; + UNICODE_STRING ValueNameString; + UNICODE_STRING KeyName; + WCHAR NameBuffer[256]; + HANDLE KeyHandle; + ULONG KeyInfoSize; + ULONG ResultSize; + PWCHAR Ptr; + NTSTATUS Status; + + wcscpy (NameBuffer, + L"\\Registry\\Machine\\Software\\Microsoft\\Windows NT\\CurrentVersion\\Image File Execution Options\\"); + Ptr = wcsrchr (SubKey->Buffer, L'\\'); + if (Ptr == NULL) + { + Ptr = SubKey->Buffer; + } + else + { + Ptr++; + } + wcscat (NameBuffer, Ptr); + RtlInitUnicodeString (&KeyName, + NameBuffer); + + InitializeObjectAttributes (&ObjectAttributes, + &KeyName, + OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + Status = NtOpenKey (&KeyHandle, + KEY_READ, + &ObjectAttributes); + if (!NT_SUCCESS(Status)) + { + DPRINT ("NtOpenKey() failed (Status %lx)\n", Status); + return Status; + } - DPRINT("LdrShutdownThread() called\n"); + KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 32; + KeyInfo = RtlAllocateHeap (RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + KeyInfoSize); + + RtlInitUnicodeString (&ValueNameString, + (PWSTR)ValueName); + Status = NtQueryValueKey (KeyHandle, + &ValueNameString, + KeyValuePartialInformation, + KeyInfo, + KeyInfoSize, + &ResultSize); + if (Status == STATUS_BUFFER_OVERFLOW) + { + KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + KeyInfo->DataLength; + RtlFreeHeap (RtlGetProcessHeap(), + 0, + KeyInfo); + KeyInfo = RtlAllocateHeap (RtlGetProcessHeap(), + HEAP_ZERO_MEMORY, + KeyInfoSize); + if (KeyInfo == NULL) + { + NtClose (KeyHandle); + return Status; + } + + Status = NtQueryValueKey (KeyHandle, + &ValueNameString, + KeyValuePartialInformation, + KeyInfo, + KeyInfoSize, + &ResultSize); + } + NtClose (KeyHandle); - RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); + if (!NT_SUCCESS(Status)) + { + if (KeyInfo != NULL) + { + RtlFreeHeap (RtlGetProcessHeap(), + 0, + KeyInfo); + } + return Status; + } - ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; - Entry = ModuleListHead->Blink; + if (KeyInfo->Type != Type) + { + RtlFreeHeap (RtlGetProcessHeap(), + 0, + KeyInfo); + return STATUS_OBJECT_TYPE_MISMATCH; + } - while (Entry != ModuleListHead) - { - Module = CONTAINING_RECORD(Entry, LDR_MODULE, InInitializationOrderModuleList); + ResultSize = BufferSize; + if (ResultSize < KeyInfo->DataLength) + { + Status = STATUS_BUFFER_OVERFLOW; + } + else + { + ResultSize = KeyInfo->DataLength; + } + RtlCopyMemory (Buffer, + &KeyInfo->Data, + ResultSize); - DPRINT(" Unloading %wZ\n", - &Module->BaseDllName); + RtlFreeHeap (RtlGetProcessHeap(), + 0, + KeyInfo); - if (Module->EntryPoint != 0) - { - PDLLMAIN_FUNC Entrypoint = (PDLLMAIN_FUNC)Module->EntryPoint; + if (ReturnedLength != NULL) + { + *ReturnedLength = ResultSize; + } - DPRINT("Calling entry point at 0x%08x\n", Entrypoint); - Entrypoint (Module->BaseAddress, - DLL_THREAD_DETACH, - NULL); - } + return Status; +} - Entry = Entry->Blink; - } - RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); +PIMAGE_BASE_RELOCATION NTAPI +LdrProcessRelocationBlock(IN PVOID Address, + IN USHORT Count, + IN PUSHORT TypeOffset, + IN ULONG_PTR Delta) +{ + SHORT Offset; + USHORT Type; + USHORT i; + PUSHORT ShortPtr; + PULONG LongPtr; - DPRINT("LdrShutdownThread() done\n"); + for (i = 0; i < Count; i++) + { + Offset = *TypeOffset & 0xFFF; + Type = *TypeOffset >> 12; + + switch (Type) + { + case IMAGE_REL_BASED_ABSOLUTE: + break; + + case IMAGE_REL_BASED_HIGH: + ShortPtr = (PUSHORT)((ULONG_PTR)Address + Offset); + *ShortPtr += HIWORD(Delta); + break; + + case IMAGE_REL_BASED_LOW: + ShortPtr = (PUSHORT)((ULONG_PTR)Address + Offset); + *ShortPtr += LOWORD(Delta); + break; + + case IMAGE_REL_BASED_HIGHLOW: + LongPtr = (PULONG)((ULONG_PTR)Address + Offset); + *LongPtr += Delta; + break; + + case IMAGE_REL_BASED_HIGHADJ: + case IMAGE_REL_BASED_MIPS_JMPADDR: + default: + DPRINT1("Unknown/unsupported fixup type %hu.\n", Type); + return NULL; + } + + TypeOffset++; + } - return STATUS_SUCCESS; + return (PIMAGE_BASE_RELOCATION)TypeOffset; } /* EOF */