/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: lib/ntdll/ldr/utils.c * PURPOSE: Process startup for PE executables * PROGRAMMERS: Jean Michault * Rex Jolliff (rex@lvcablemodem.com) * Michael Martin */ /* * TODO: * - Handle loading flags correctly * - Handle errors correctly (unload dll's) * - Implement a faster way to find modules (hash table) * - any more ?? */ /* INCLUDES *****************************************************************/ #include #define NDEBUG #include #define LDRP_PROCESS_CREATION_TIME 0xffff #define RVA(m, b) ((PVOID)((ULONG_PTR)(b) + (ULONG_PTR)(m))) /* GLOBALS *******************************************************************/ #ifdef NDEBUG #define TRACE_LDR(...) if (RtlGetNtGlobalFlags() & FLG_SHOW_LDR_SNAPS) { DbgPrint("(LDR:%s:%d) ",__FILE__,__LINE__); DbgPrint(__VA_ARGS__); } #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 BOOLEAN LdrpDllShutdownInProgress = FALSE; 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; /* PROTOTYPES ****************************************************************/ 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); static NTSTATUS LdrpUnloadModule(PLDR_DATA_TABLE_ENTRY Module, BOOLEAN Unload); NTSTATUS find_actctx_dll( LPCWSTR libname, WCHAR *fulldosname ); NTSTATUS create_module_activation_context( LDR_DATA_TABLE_ENTRY *module ); /* FUNCTIONS *****************************************************************/ 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 != LDRP_PROCESS_CREATION_TIME) { 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 != LDRP_PROCESS_CREATION_TIME) { 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 == LDRP_PROCESS_CREATION_TIME) { 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++; } } } } 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 PWSTR LdrpQueryAppPaths(IN PCWSTR ImageName) { PKEY_VALUE_PARTIAL_INFORMATION KeyInfo; OBJECT_ATTRIBUTES ObjectAttributes; WCHAR SearchPathBuffer[5*MAX_PATH]; UNICODE_STRING ValueNameString; UNICODE_STRING KeyName; WCHAR NameBuffer[MAX_PATH]; ULONG KeyInfoSize; ULONG ResultSize; PWCHAR Backslash; HANDLE KeyHandle; NTSTATUS Status; PWSTR Path = NULL; _snwprintf(NameBuffer, sizeof(NameBuffer) / sizeof(WCHAR), L"\\Registry\\Machine\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\%s", ImageName); 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 NULL; } KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 256 * sizeof(WCHAR); KeyInfo = RtlAllocateHeap(RtlGetProcessHeap(), 0, KeyInfoSize); if (KeyInfo == NULL) { DPRINT("RtlAllocateHeap() failed\n"); NtClose(KeyHandle); return NULL; } RtlInitUnicodeString(&ValueNameString, L"Path"); Status = NtQueryValueKey(KeyHandle, &ValueNameString, KeyValuePartialInformation, KeyInfo, KeyInfoSize, &ResultSize); if (!NT_SUCCESS(Status)) { NtClose(KeyHandle); RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo); return NULL; } RtlCopyMemory(SearchPathBuffer, &KeyInfo->Data, KeyInfo->DataLength); /* Free KeyInfo memory, we won't need it anymore */ RtlFreeHeap(RtlGetProcessHeap(), 0, KeyInfo); /* Close the key handle */ NtClose(KeyHandle); /* get application running path */ wcscat(SearchPathBuffer, L";"); wcscat(SearchPathBuffer, NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer); // FIXME: Don't rely on it being NULL-terminated!!! /* Remove trailing backslash */ Backslash = wcsrchr(SearchPathBuffer, L'\\'); if (Backslash) Backslash = L'\0'; wcscat(SearchPathBuffer, L";"); wcscat(SearchPathBuffer, SharedUserData->NtSystemRoot); wcscat(SearchPathBuffer, L"\\system32;"); wcscat(SearchPathBuffer, SharedUserData->NtSystemRoot); wcscat(SearchPathBuffer, L";."); /* Copy it to the heap allocd memory */ Path = RtlAllocateHeap(RtlGetProcessHeap(), 0, wcslen(SearchPathBuffer) * sizeof(WCHAR)); if (!Path) { DPRINT1("RtlAllocateHeap() failed\n"); return NULL; } wcscpy(Path, SearchPathBuffer); return Path; } 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, InLoadOrderLinks); if (Module->LoadCount == LDRP_PROCESS_CREATION_TIME && 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\n", 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, 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 * * DESCRIPTION * 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 * * RETURN VALUE * 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 */ static VOID LdrAdjustDllName (PUNICODE_STRING FullDllName, PUNICODE_STRING DllName, BOOLEAN BaseName) { WCHAR Buffer[MAX_PATH]; ULONG Length; PWCHAR Extension; PWCHAR Pointer; Length = DllName->Length / sizeof(WCHAR); 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)); Buffer[Length] = L'\0'; } else { /* 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; } else { /* name without extension - assume that it is .dll */ memmove (Buffer + Length, L".dll", 10); } RtlCreateUnicodeString(FullDllName, Buffer); } PLDR_DATA_TABLE_ENTRY LdrAddModuleEntry(PVOID ImageBase, PIMAGE_NT_HEADERS NTHeaders, PWSTR FullDosName) { PLDR_DATA_TABLE_ENTRY Module; 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 = LDRP_PROCESS_CREATION_TIME; } 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->InLoadOrderLinks); RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); return(Module); } static NTSTATUS LdrpMapKnownDll(IN PUNICODE_STRING DllName, OUT PUNICODE_STRING FullDosName, OUT PHANDLE SectionHandle) { OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; DPRINT("LdrpMapKnownDll() called\n"); if (LdrpKnownDllsDirHandle == NULL) { DPRINT("Invalid 'KnownDlls' directory\n"); return STATUS_UNSUCCESSFUL; } 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 0x%08lx)\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) { FullDosName->Length = 0; FullDosName->MaximumLength = 0; return STATUS_SUCCESS; } 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 = NULL; WCHAR *ImagePathNameBufferPtr = NULL; WCHAR DosName[MAX_PATH]; UNICODE_STRING FullNtFileName; UNICODE_STRING PathEnvironmentVar_U; UNICODE_STRING PathName_U; OBJECT_ATTRIBUTES FileObjectAttributes; HANDLE FileHandle; char BlockBuffer [1024]; PIMAGE_DOS_HEADER DosHeader; PIMAGE_NT_HEADERS NTHeaders; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status; ULONG len; ULONG ImagePathLen; DPRINT("LdrpMapDllImageFile() called\n"); if (SearchPath == NULL) { /* get application running path */ ImagePathNameBufferPtr = NtCurrentPeb()->ProcessParameters->ImagePathName.Buffer; /* Length of ImagePathName */ ImagePathLen = wcslen(ImagePathNameBufferPtr); /* Subtract application name leaveing only the directory length */ while (ImagePathLen && ImagePathNameBufferPtr[ImagePathLen - 1] != L'\\') ImagePathLen--; /* Length of directory + semicolon */ len = ImagePathLen + 1; /* Length of SystemRoot + "//system32" + semicolon*/ len += wcslen(SharedUserData->NtSystemRoot) + 10; /* Length of SystemRoot + semicolon */ len += wcslen(SharedUserData->NtSystemRoot) + 1; RtlInitUnicodeString (&PathName_U, L"PATH"); PathEnvironmentVar_U.Length = 0; PathEnvironmentVar_U.MaximumLength = 0; PathEnvironmentVar_U.Buffer = NULL; /* Get the path environment variable */ Status = RtlQueryEnvironmentVariable_U(NULL, &PathName_U, &PathEnvironmentVar_U); /* Check that valid information was returned */ if ((Status == STATUS_BUFFER_TOO_SMALL) && (PathEnvironmentVar_U.Length > 0)) { /* Allocate memory for the path env var */ PathEnvironmentVar_U.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, PathEnvironmentVar_U.Length + sizeof(WCHAR)); if (!PathEnvironmentVar_U.Buffer) { DPRINT1("Fatal! Out of Memory!!\n"); return STATUS_NO_MEMORY; } PathEnvironmentVar_U.MaximumLength = PathEnvironmentVar_U.Length + sizeof(WCHAR); /* Retry */ Status = RtlQueryEnvironmentVariable_U(NULL, &PathName_U, &PathEnvironmentVar_U); if (!NT_SUCCESS(Status)) { DPRINT1("Unable to get path environment string!\n"); ASSERT(FALSE); } /* Length of path evn var + semicolon */ len += (PathEnvironmentVar_U.Length / sizeof(WCHAR)) + 1; } /* Allocate the size needed to hold all the above paths + period */ SearchPathBuffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, (len + 2) * sizeof(WCHAR)); if (!SearchPathBuffer) { DPRINT1("Fatal! Out of Memory!!\n"); return STATUS_NO_MEMORY; } wcsncpy(SearchPathBuffer, ImagePathNameBufferPtr, ImagePathLen); wcscat (SearchPathBuffer, L";"); wcscat (SearchPathBuffer, SharedUserData->NtSystemRoot); wcscat (SearchPathBuffer, L"\\system32;"); wcscat (SearchPathBuffer, SharedUserData->NtSystemRoot); wcscat (SearchPathBuffer, L";"); if (PathEnvironmentVar_U.Buffer) { wcscat (SearchPathBuffer, PathEnvironmentVar_U.Buffer); wcscat (SearchPathBuffer, L";"); RtlFreeHeap(RtlGetProcessHeap(), 0, PathEnvironmentVar_U.Buffer); } wcscat (SearchPathBuffer, L"."); SearchPath = SearchPathBuffer; } if (RtlDosSearchPath_U (SearchPath, DllName->Buffer, NULL, MAX_PATH, DosName, NULL) == 0) { /* try to find active context dll */ Status = find_actctx_dll(DllName->Buffer, DosName); if(Status == STATUS_SUCCESS) DPRINT("found %S for %S\n", DosName,DllName->Buffer); else return STATUS_DLL_NOT_FOUND; } if (!RtlDosPathNameToNtPathName_U (DosName, &FullNtFileName, NULL, NULL)) { DPRINT("Dll %wZ not found!\n", DllName); return STATUS_DLL_NOT_FOUND; } DPRINT("FullNtFileName %wZ\n", &FullNtFileName); InitializeObjectAttributes(&FileObjectAttributes, &FullNtFileName, 0, NULL, NULL); DPRINT("Opening dll \"%wZ\"\n", &FullNtFileName); Status = NtOpenFile(&FileHandle, GENERIC_READ|SYNCHRONIZE, &FileObjectAttributes, &IoStatusBlock, FILE_SHARE_READ, FILE_SYNCHRONOUS_IO_NONALERT); if (!NT_SUCCESS(Status)) { DPRINT1("Dll open of %wZ failed: Status = 0x%08lx\n", &FullNtFileName, Status); RtlFreeHeap (RtlGetProcessHeap (), 0, FullNtFileName.Buffer); return Status; } RtlFreeHeap (RtlGetProcessHeap (), 0, FullNtFileName.Buffer); if (!MapAsDataFile) { Status = NtReadFile(FileHandle, NULL, NULL, NULL, &IoStatusBlock, BlockBuffer, sizeof(BlockBuffer), NULL, NULL); if (!NT_SUCCESS(Status)) { DPRINT("Dll header read failed: Status = 0x%08lx\n", Status); NtClose(FileHandle); return Status; } /* * Overlay DOS and NT headers structures to the * buffer with DLL's header raw data. */ DosHeader = (PIMAGE_DOS_HEADER) BlockBuffer; NTHeaders = (PIMAGE_NT_HEADERS) (BlockBuffer + DosHeader->e_lfanew); /* * Check it is a PE image file. */ if ((DosHeader->e_magic != IMAGE_DOS_SIGNATURE) || (DosHeader->e_lfanew == 0L) || (*(PULONG)(NTHeaders) != IMAGE_NT_SIGNATURE)) { DPRINT("NTDLL format invalid\n"); NtClose(FileHandle); return STATUS_UNSUCCESSFUL; } } /* * Create a section for dll. */ Status = NtCreateSection(SectionHandle, SECTION_ALL_ACCESS, NULL, NULL, PAGE_READONLY, MapAsDataFile ? SEC_COMMIT : SEC_IMAGE, FileHandle); NtClose(FileHandle); if (!NT_SUCCESS(Status)) { DPRINT("NTDLL create section failed: Status = 0x%08lx\n", Status); return Status; } RtlCreateUnicodeString(FullDosName, DosName); return Status; } /*************************************************************************** * NAME EXPORTED * LdrLoadDll * * DESCRIPTION * * ARGUMENTS * * RETURN VALUE * * REVISIONS * * NOTE * * @implemented */ NTSTATUS NTAPI LdrLoadDll (IN PWSTR SearchPath OPTIONAL, IN PULONG LoadFlags OPTIONAL, IN PUNICODE_STRING Name, OUT PVOID *BaseAddress /* also known as HMODULE*, and PHANDLE 'DllHandle' */) { NTSTATUS Status; PLDR_DATA_TABLE_ENTRY Module; ULONG_PTR cookie; PPEB Peb = NtCurrentPeb(); TRACE_LDR("LdrLoadDll loading %wZ%S%S with flags %d\n", Name, SearchPath ? L" from " : L"", SearchPath ? SearchPath : L"", LoadFlags ? *LoadFlags : 0); Status = LdrpLoadModule(SearchPath, LoadFlags ? *LoadFlags : 0, Name, &Module, BaseAddress); if (NT_SUCCESS(Status) && (!LoadFlags || 0 == (*LoadFlags & LOAD_LIBRARY_AS_DATAFILE))) { if (!create_module_activation_context( Module )) { RtlActivateActivationContext(0, Module->EntryPointActivationContext, &cookie); } if (!(Module->Flags & LDRP_PROCESS_ATTACH_CALLED)) { RtlEnterCriticalSection(Peb->LoaderLock); Status = LdrpAttachProcess(); RtlLeaveCriticalSection(Peb->LoaderLock); } if (Module->EntryPointActivationContext) RtlDeactivateActivationContext(0, cookie); } if ((!Module) && (NT_SUCCESS(Status))) return Status; *BaseAddress = NT_SUCCESS(Status) ? Module->DllBase : NULL; return Status; } /*************************************************************************** * NAME EXPORTED * LdrFindEntryForAddress * * DESCRIPTION * * ARGUMENTS * * RETURN VALUE * * REVISIONS * * NOTE * * @implemented */ NTSTATUS NTAPI LdrFindEntryForAddress(PVOID Address, PLDR_DATA_TABLE_ENTRY *Module) { PLIST_ENTRY ModuleListHead; PLIST_ENTRY Entry; PLDR_DATA_TABLE_ENTRY ModulePtr; DPRINT("LdrFindEntryForAddress(Address %p)\n", Address); 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); } while (Entry != ModuleListHead) { ModulePtr = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); DPRINT("Scanning %wZ at %p\n", &ModulePtr->BaseDllName, ModulePtr->DllBase); if ((Address >= ModulePtr->DllBase) && ((ULONG_PTR)Address <= ((ULONG_PTR)ModulePtr->DllBase + ModulePtr->SizeOfImage))) { *Module = ModulePtr; RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); return(STATUS_SUCCESS); } Entry = Entry->Flink; } DPRINT("Failed to find module entry.\n"); RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); return(STATUS_NO_MORE_ENTRIES); } /*************************************************************************** * NAME LOCAL * LdrFindEntryForName * * DESCRIPTION * * ARGUMENTS * * RETURN VALUE * * REVISIONS * * NOTE * */ static NTSTATUS LdrFindEntryForName(PUNICODE_STRING Name, PLDR_DATA_TABLE_ENTRY *Module, BOOLEAN Ref) { PLIST_ENTRY ModuleListHead; PLIST_ENTRY Entry; PLDR_DATA_TABLE_ENTRY ModulePtr; BOOLEAN ContainsPath; UNICODE_STRING AdjustedName; 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); } ContainsPath = (Name->Length >= 2 * sizeof(WCHAR) && L':' == Name->Buffer[1]); LdrAdjustDllName (&AdjustedName, Name, !ContainsPath); if (LdrpLastModule) { if ((! ContainsPath && 0 == RtlCompareUnicodeString(&LdrpLastModule->BaseDllName, &AdjustedName, TRUE)) || (ContainsPath && 0 == RtlCompareUnicodeString(&LdrpLastModule->FullDllName, &AdjustedName, TRUE))) { *Module = LdrpLastModule; if (Ref && (*Module)->LoadCount != LDRP_PROCESS_CREATION_TIME) { (*Module)->LoadCount++; } RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); RtlFreeUnicodeString(&AdjustedName); return(STATUS_SUCCESS); } } while (Entry != ModuleListHead) { ModulePtr = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); 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 != LDRP_PROCESS_CREATION_TIME) { 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 = 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, NULL, &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; } /********************************************************************** * NAME LOCAL * LdrGetExportByOrdinal * * DESCRIPTION * * ARGUMENTS * * RETURN VALUE * * REVISIONS * * NOTE * */ static PVOID LdrGetExportByOrdinal ( PVOID BaseAddress, ULONG Ordinal ) { 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 %lu) = %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 * * DESCRIPTION * * ARGUMENTS * * RETURN VALUE * * REVISIONS * * NOTE * AddressOfNames and AddressOfNameOrdinals are paralell tables, * both with NumberOfNames entries. * */ static PVOID LdrGetExportByName(PVOID BaseAddress, PUCHAR SymbolName, WORD Hint) { PIMAGE_EXPORT_DIRECTORY ExportDir; PDWORD * ExFunctions; PDWORD * ExNames; USHORT * ExOrdinals; PVOID ExName; ULONG Ordinal; PVOID Function; LONG minn, maxn; ULONG ExportDirSize; DPRINT("LdrGetExportByName %p %s %hu\n", BaseAddress, SymbolName, Hint); ExportDir = (PIMAGE_EXPORT_DIRECTORY) RtlImageDirectoryEntryToData(BaseAddress, TRUE, IMAGE_DIRECTORY_ENTRY_EXPORT, &ExportDirSize); if (ExportDir == NULL) { DPRINT1("LdrGetExportByName(): no export directory, " "can't lookup %s/%hu!\n", SymbolName, Hint); 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); ExOrdinals = (USHORT *)RVA(BaseAddress, ExportDir->AddressOfNameOrdinals); ExFunctions = (PDWORD *)RVA(BaseAddress, ExportDir->AddressOfFunctions); /* * Check the hint first */ if (Hint < ExportDir->NumberOfNames) { 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; } } /* * Binary search */ minn = 0; maxn = ExportDir->NumberOfNames - 1; while (minn <= maxn) { 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; } } DPRINT("LdrGetExportByName(): failed to find %s\n",SymbolName); return (PVOID)NULL; } /********************************************************************** * NAME LOCAL * LdrPerformRelocations * * DESCRIPTION * Relocate a DLL's memory image. * * ARGUMENTS * * RETURN VALUE * * REVISIONS * * NOTE * */ static NTSTATUS LdrPerformRelocations(PIMAGE_NT_HEADERS NTHeaders, PVOID ImageBase) { 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_SUCCESS; } 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((ULONG_PTR)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 SearchPath, PCHAR Name, PLDR_DATA_TABLE_ENTRY* Module, BOOLEAN Load) { ANSI_STRING AnsiDllName; UNICODE_STRING DllName; NTSTATUS Status; DPRINT("LdrpGetOrLoadModule() called for %s\n", Name); RtlInitAnsiString(&AnsiDllName, Name); Status = RtlAnsiStringToUnicodeString(&DllName, &AnsiDllName, TRUE); if (!NT_SUCCESS(Status)) { return Status; } Status = LdrFindEntryForName (&DllName, Module, Load); if (Load && !NT_SUCCESS(Status)) { Status = LdrpLoadModule(SearchPath, 0, &DllName, Module, NULL); if (NT_SUCCESS(Status)) { Status = LdrFindEntryForName (&DllName, Module, FALSE); } if (!NT_SUCCESS(Status)) { ULONG ErrorResponse; ULONG_PTR ErrorParameter = (ULONG_PTR)&AnsiDllName; DPRINT1("failed to load %wZ\n", &DllName); NtRaiseHardError(STATUS_DLL_NOT_FOUND, 1, 1, &ErrorParameter, OptionOk, &ErrorResponse); } } RtlFreeUnicodeString (&DllName); return Status; } void RtlpRaiseImportNotFound(CHAR *FuncName, ULONG Ordinal, PUNICODE_STRING DllName) { ULONG ErrorResponse; ULONG_PTR ErrorParameters[2]; ANSI_STRING ProcNameAnsi; UNICODE_STRING ProcName; CHAR Buffer[8]; if (!FuncName) { _snprintf(Buffer, 8, "# %ld", Ordinal); FuncName = Buffer; } RtlInitAnsiString(&ProcNameAnsi, FuncName); RtlAnsiStringToUnicodeString(&ProcName, &ProcNameAnsi, TRUE); ErrorParameters[0] = (ULONG_PTR)&ProcName; ErrorParameters[1] = (ULONG_PTR)DllName; NtRaiseHardError(STATUS_ENTRYPOINT_NOT_FOUND, 2, 3, ErrorParameters, OptionOk, &ErrorResponse); RtlFreeUnicodeString(&ProcName); } 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++; } /* No need to fixup anything if IAT is empty */ if (IATSize == 0) return STATUS_SUCCESS; /* 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); RtlpRaiseImportNotFound(NULL, Ordinal, &ImportedModule->FullDllName); return STATUS_ENTRYPOINT_NOT_FOUND; } } 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); RtlpRaiseImportNotFound((CHAR*)pe_name->Name, 0, &ImportedModule->FullDllName); return STATUS_ENTRYPOINT_NOT_FOUND; } } 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(%p '%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 %p '%wZ', %p '%wZ', '%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 * * DESCRIPTION * Compute the entry point for every symbol the DLL imports * from other modules. * * ARGUMENTS * * RETURN VALUE * * REVISIONS * * NOTE * */ static NTSTATUS LdrFixupImports(IN PWSTR SearchPath OPTIONAL, IN PLDR_DATA_TABLE_ENTRY Module) { 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 = STATUS_SUCCESS; PLDR_DATA_TABLE_ENTRY ImportedModule; PCHAR ImportedName; PWSTR ModulePath; ULONG Size; ULONG_PTR cookie; DPRINT("LdrFixupImports(SearchPath %S, Module %p)\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 dynamically load %wZ which contains a TLS directory\n", &Module->BaseDllName); TlsDirectory = NULL; } } if (!create_module_activation_context( Module )) { if (Module->EntryPointActivationContext == NULL) { DPRINT("EntryPointActivationContext has not be allocated\n"); DPRINT("Module->DllBaseName %wZ\n", Module->BaseDllName); } RtlActivateActivationContext( 0, Module->EntryPointActivationContext, &cookie ); } /* * Process each import module. */ 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 %p\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 %p\n", ImportModuleDirectory); ImportModuleDirectoryCurrent = ImportModuleDirectory; while (ImportModuleDirectoryCurrent->Name) { ImportedName = (PCHAR)Module->DllBase + ImportModuleDirectoryCurrent->Name; TRACE_LDR("%wZ imports functions from %s\n", &Module->BaseDllName, ImportedName); if (SearchPath == NULL) { ModulePath = LdrpQueryAppPaths(Module->BaseDllName.Buffer); Status = LdrpGetOrLoadModule(ModulePath, ImportedName, &ImportedModule, TRUE); if (ModulePath != NULL) RtlFreeHeap(RtlGetProcessHeap(), 0, ModulePath); if (NT_SUCCESS(Status)) goto Success; } Status = LdrpGetOrLoadModule(SearchPath, ImportedName, &ImportedModule, TRUE); if (!NT_SUCCESS(Status)) { DPRINT1("failed to load %s\n", ImportedName); break; } Success: 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); break; } ImportModuleDirectoryCurrent++; } if (!NT_SUCCESS(Status)) { NTSTATUS errorStatus = Status; while (ImportModuleDirectoryCurrent >= ImportModuleDirectory) { ImportedName = (PCHAR)Module->DllBase + ImportModuleDirectoryCurrent->Name; Status = LdrpGetOrLoadModule(NULL, ImportedName, &ImportedModule, FALSE); if (NT_SUCCESS(Status) && Module != ImportedModule) { Status = LdrpUnloadModule(ImportedModule, FALSE); if (!NT_SUCCESS(Status)) DPRINT1("unable to unload %s\n", ImportedName); } ImportModuleDirectoryCurrent--; } return errorStatus; } } if (TlsDirectory && TlsSize > 0) { LdrpAcquireTlsSlot(Module, TlsSize, FALSE); } if (Module->EntryPointActivationContext) RtlDeactivateActivationContext( 0, cookie ); return STATUS_SUCCESS; } /********************************************************************** * NAME * LdrPEStartup * * DESCRIPTION * 1. Relocate, if needed the EXE. * 2. Fixup any imported symbol. * 3. Compute the EXE's entry point. * * ARGUMENTS * 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. * * 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, PLDR_DATA_TABLE_ENTRY* Module, PWSTR FullDosName) { NTSTATUS Status; PEPFUNC EntryPoint = NULL; PIMAGE_DOS_HEADER DosHeader; PIMAGE_NT_HEADERS NTHeaders; PLDR_DATA_TABLE_ENTRY tmpModule; PVOID ActivationContextStack; DPRINT("LdrPEStartup(ImageBase %p SectionHandle %p)\n", ImageBase, SectionHandle); /* * Overlay DOS and WNT headers structures * to the DLL's image. */ DosHeader = (PIMAGE_DOS_HEADER) ImageBase; NTHeaders = (PIMAGE_NT_HEADERS) ((ULONG_PTR)ImageBase + DosHeader->e_lfanew); /* * If the base address is different from the * one the DLL is actually loaded, perform any * relocation. */ if (ImageBase != (PVOID) NTHeaders->OptionalHeader.ImageBase) { DPRINT("LDR: Performing relocations\n"); Status = LdrPerformRelocations(NTHeaders, ImageBase); if (!NT_SUCCESS(Status)) { 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; } /* Allocate memory for the ActivationContextStack */ /* FIXME: Verify RtlAllocateActivationContextStack behavior */ Status = RtlAllocateActivationContextStack(&ActivationContextStack); if (NT_SUCCESS(Status)) { DPRINT("ActivationContextStack %x\n",ActivationContextStack); DPRINT("ActiveFrame %x\n", ((PACTIVATION_CONTEXT_STACK)ActivationContextStack)->ActiveFrame); NtCurrentTeb()->ActivationContextStackPointer = ActivationContextStack; NtCurrentTeb()->ActivationContextStackPointer->ActiveFrame = NULL; } else DPRINT1("Warning: Unable to allocate ActivationContextStack\n"); /* * If the DLL's imports symbols from other * modules, fixup the imported calls entry points. */ 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)) { LdrpTlsCallback(*Module, DLL_PROCESS_ATTACH); } RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); if (!NT_SUCCESS(Status)) { return NULL; } /* * Compute the DLL's entry point's address. */ DPRINT("ImageBase = %p\n", ImageBase); DPRINT("AddressOfEntryPoint = 0x%lx\n",(ULONG)NTHeaders->OptionalHeader.AddressOfEntryPoint); if (NTHeaders->OptionalHeader.AddressOfEntryPoint != 0) { EntryPoint = (PEPFUNC) ((ULONG_PTR)ImageBase + NTHeaders->OptionalHeader.AddressOfEntryPoint); } DPRINT("LdrPEStartup() = %p\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; SIZE_T ViewSize; PVOID ImageBase; PIMAGE_NT_HEADERS NtHeaders; BOOLEAN MappedAsDataFile; PVOID ArbitraryUserPointer; 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; ArbitraryUserPointer = NtCurrentTeb()->NtTib.ArbitraryUserPointer; NtCurrentTeb()->NtTib.ArbitraryUserPointer = FullDosName.Buffer; Status = NtMapViewOfSection(SectionHandle, NtCurrentProcess(), &ImageBase, 0, 0, NULL, &ViewSize, ViewShare, 0, PAGE_READONLY); NtCurrentTeb()->NtTib.ArbitraryUserPointer = ArbitraryUserPointer; if (!NT_SUCCESS(Status)) { DPRINT1("map view of section failed (Status 0x%08lx)\n", Status); RtlFreeUnicodeString(&FullDosName); NtClose(SectionHandle); return(Status); } if (NULL != BaseAddress) { *BaseAddress = ImageBase; } if (!MappedAsDataFile) { /* 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 (%lx -> %p) %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); NtUnmapViewOfSection (NtCurrentProcess (), ImageBase); NtClose (SectionHandle); RtlFreeUnicodeString (&FullDosName); RtlFreeUnicodeString (&(*Module)->FullDllName); RtlFreeUnicodeString (&(*Module)->BaseDllName); RemoveEntryList (&(*Module)->InLoadOrderLinks); RtlFreeHeap (RtlGetProcessHeap (), 0, Module); return Status; } RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); InsertTailList(&NtCurrentPeb()->Ldr->InInitializationOrderModuleList, &(*Module)->InInitializationOrderModuleList); RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); } return STATUS_SUCCESS; } static NTSTATUS LdrpUnloadModule(PLDR_DATA_TABLE_ENTRY Module, BOOLEAN Unload) { PIMAGE_IMPORT_DESCRIPTOR ImportModuleDirectory; PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundImportDescriptor; PIMAGE_BOUND_IMPORT_DESCRIPTOR BoundImportDescriptorCurrent; PCHAR ImportedName; PLDR_DATA_TABLE_ENTRY ImportedModule; NTSTATUS Status = 0; LONG LoadCount; ULONG Size; if (Unload) { RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); } LoadCount = LdrpDecrementLoadCount(Module, Unload); TRACE_LDR("Unload %wZ, LoadCount %d\n", &Module->BaseDllName, LoadCount); if (LoadCount == 0) { /* ?????????????????? */ } else if (!(Module->Flags & LDRP_STATIC_LINK) && LoadCount == 1) { 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++; } } } } if (Unload) { if (!(Module->Flags & LDRP_STATIC_LINK)) { LdrpDetachProcess(FALSE); } RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); } return STATUS_SUCCESS; } /* * @implemented */ NTSTATUS NTAPI LdrUnloadDll (IN PVOID BaseAddress) { PLDR_DATA_TABLE_ENTRY Module; NTSTATUS Status; if (BaseAddress == NULL) return STATUS_SUCCESS; if (LdrMappedAsDataFile(&BaseAddress)) { Status = NtUnmapViewOfSection(NtCurrentProcess(), BaseAddress); } else { Status = LdrFindEntryForAddress(BaseAddress, &Module); if (NT_SUCCESS(Status)) { TRACE_LDR("LdrUnloadDll, , unloading %wZ\n", &Module->BaseDllName); Status = LdrpUnloadModule(Module, TRUE); } } 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 %p)\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, InLoadOrderLinks); DPRINT("BaseDllName %wZ BaseAddress %p\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 0x%lx\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; } /* * @implemented */ NTSTATUS NTAPI LdrAddRefDll(IN ULONG Flags, IN PVOID BaseAddress) { PLIST_ENTRY ModuleListHead; PLIST_ENTRY Entry; PLDR_DATA_TABLE_ENTRY Module; NTSTATUS Status; if (Flags & ~(LDR_PIN_MODULE)) { return STATUS_INVALID_PARAMETER; } 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, InLoadOrderLinks); if (Module->DllBase == BaseAddress) { if (Flags & LDR_PIN_MODULE) { Module->Flags |= LDRP_STATIC_LINK; } else { LdrpIncrementLoadCount(Module, FALSE); } Status = STATUS_SUCCESS; break; } Entry = Entry->Flink; } RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); return Status; } /* * @implemented */ PVOID NTAPI RtlPcToFileHeader(IN PVOID PcValue, PVOID* BaseOfImage) { PLIST_ENTRY ModuleListHead; PLIST_ENTRY Entry; PLDR_DATA_TABLE_ENTRY Module; PVOID ImageBase = NULL; RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; Entry = ModuleListHead->Flink; while (Entry != ModuleListHead) { Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if ((ULONG_PTR)PcValue >= (ULONG_PTR)Module->DllBase && (ULONG_PTR)PcValue < (ULONG_PTR)Module->DllBase + Module->SizeOfImage) { ImageBase = Module->DllBase; break; } Entry = Entry->Flink; } RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); *BaseOfImage = ImageBase; return ImageBase; } /* * @implemented */ NTSTATUS NTAPI LdrGetProcedureAddress (IN PVOID BaseAddress, IN PANSI_STRING Name, IN ULONG Ordinal, OUT PVOID *ProcedureAddress) { NTSTATUS Status = STATUS_PROCEDURE_NOT_FOUND; if (Name && Name->Length) { TRACE_LDR("LdrGetProcedureAddress by NAME - %Z\n", Name); } else { TRACE_LDR("LdrGetProcedureAddress by ORDINAL - %d\n", Ordinal); } DPRINT("LdrGetProcedureAddress (BaseAddress %p Name %Z Ordinal %lu ProcedureAddress %p)\n", BaseAddress, Name, Ordinal, ProcedureAddress); _SEH2_TRY { if (Name && Name->Length) { /* by name */ *ProcedureAddress = LdrGetExportByName(BaseAddress, (PUCHAR)Name->Buffer, 0xffff); if (*ProcedureAddress != NULL) { Status = STATUS_SUCCESS; } DPRINT("LdrGetProcedureAddress: Can't resolve symbol '%Z'\n", Name); } else { /* by ordinal */ Ordinal &= 0x0000FFFF; *ProcedureAddress = LdrGetExportByOrdinal(BaseAddress, (WORD)Ordinal); if (*ProcedureAddress) { Status = STATUS_SUCCESS; } DPRINT("LdrGetProcedureAddress: Can't resolve symbol @%lu\n", Ordinal); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = STATUS_DLL_NOT_FOUND; } _SEH2_END; return Status; } /********************************************************************** * 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); if (UnloadAll) LdrpDllShutdownInProgress = TRUE; CallingCount++; ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; Entry = ModuleListHead->Blink; while (Entry != ModuleListHead) { Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList); if (((UnloadAll && Module->LoadCount == LDRP_PROCESS_CREATION_TIME) || 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 == LDRP_PROCESS_CREATION_TIME ? 1 : 0)); } else { TRACE_LDR("Unload %wZ\n", &Module->BaseDllName); } Entry = ModuleListHead->Blink; } else { Entry = Entry->Blink; } } 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 != LDRP_PROCESS_CREATION_TIME) || Module->LoadCount == 0)) { /* remove the module entry from the list */ RemoveEntryList (&Module->InLoadOrderLinks); 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"); } /********************************************************************** * 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_DATA_TABLE_ENTRY Module; BOOLEAN Result; NTSTATUS Status = STATUS_SUCCESS; DPRINT("LdrpAttachProcess() called for %wZ\n", &ExeModule->BaseDllName); ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; Entry = ModuleListHead->Flink; while (Entry != ModuleListHead) { 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 == LDRP_PROCESS_CREATION_TIME ? 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 */ BOOLEAN NTAPI RtlDllShutdownInProgress (VOID) { return LdrpDllShutdownInProgress; } /* * @implemented */ NTSTATUS NTAPI LdrShutdownProcess (VOID) { LdrpDetachProcess(TRUE); return STATUS_SUCCESS; } /* * @implemented */ NTSTATUS LdrpAttachThread (VOID) { PLIST_ENTRY ModuleListHead; PLIST_ENTRY Entry; PLDR_DATA_TABLE_ENTRY Module; NTSTATUS Status; DPRINT("LdrpAttachThread() called for %wZ\n", &ExeModule->BaseDllName); 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, InLoadOrderLinks); LdrpTlsCallback(Module, DLL_THREAD_ATTACH); } RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); DPRINT("LdrpAttachThread() done\n"); return Status; } /* * @implemented */ NTSTATUS NTAPI LdrShutdownThread (VOID) { PLIST_ENTRY ModuleListHead; PLIST_ENTRY Entry; PLDR_DATA_TABLE_ENTRY Module; DPRINT("LdrShutdownThread() called for %wZ\n", &ExeModule->BaseDllName); RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); ModuleListHead = &NtCurrentPeb()->Ldr->InInitializationOrderModuleList; Entry = ModuleListHead->Blink; 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 detaching\n", &Module->BaseDllName, Module->EntryPoint); LdrpCallDllEntry(Module, DLL_THREAD_DETACH, NULL); } Entry = Entry->Blink; } RtlLeaveCriticalSection (NtCurrentPeb()->LoaderLock); if (LdrpTlsArray) { RtlFreeHeap (RtlGetProcessHeap(), 0, NtCurrentTeb()->ThreadLocalStoragePointer); } DPRINT("LdrShutdownThread() done\n"); return STATUS_SUCCESS; } /*************************************************************************** * 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) { 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("LdrQueryProcessModuleInformation() called\n"); // FIXME: This code is ultra-duplicated. see lib\rtl\dbgbuffer.c RtlEnterCriticalSection (NtCurrentPeb()->LoaderLock); if (ModuleInformation == NULL || Size == 0) { Status = STATUS_INFO_LENGTH_MISMATCH; } else { ModuleInformation->NumberOfModules = 0; ModulePtr = &ModuleInformation->Modules[0]; Status = STATUS_SUCCESS; } ModuleListHead = &NtCurrentPeb()->Ldr->InLoadOrderModuleList; Entry = ModuleListHead->Flink; while (Entry != ModuleListHead) { Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks); DPRINT(" Module %wZ\n", &Module->FullDllName); if (UsedSize > Size) { Status = STATUS_INFO_LENGTH_MISMATCH; } else if (ModuleInformation != NULL) { ModulePtr->Section = 0; ModulePtr->MappedBase = NULL; // FIXME: ?? ModulePtr->ImageBase = Module->DllBase; ModulePtr->ImageSize = Module->SizeOfImage; ModulePtr->Flags = Module->Flags; ModulePtr->LoadOrderIndex = 0; // FIXME: ?? ModulePtr->InitOrderIndex = 0; // FIXME: ?? ModulePtr->LoadCount = Module->LoadCount; AnsiString.Length = 0; AnsiString.MaximumLength = 256; AnsiString.Buffer = ModulePtr->FullPathName; RtlUnicodeStringToAnsiString(&AnsiString, &Module->FullDllName, FALSE); p = strrchr(ModulePtr->FullPathName, '\\'); if (p != NULL) ModulePtr->OffsetToFileName = p - ModulePtr->FullPathName + 1; else ModulePtr->OffsetToFileName = 0; ModulePtr++; ModuleInformation->NumberOfModules++; } 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++) { Sum += (ULONG)*Ptr; if (HIWORD(Sum) != 0) { Sum = LOWORD(Sum) + HIWORD(Sum); } Ptr++; } if (ImageSize & 1) { Sum += (ULONG)*((PUCHAR)Ptr); if (HIWORD(Sum) != 0) { Sum = LOWORD(Sum) + HIWORD(Sum); } } CalcSum = (USHORT)(LOWORD(Sum) + HIWORD(Sum)); /* 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) { 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++; } return ResidentSize; } /*************************************************************************** * 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; SIZE_T 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; } 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; } 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; } NtUnmapViewOfSection (NtCurrentProcess (), BaseAddress); NtClose (SectionHandle); return Status; } /*************************************************************************** * 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) { 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; } KeyInfoSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 32; KeyInfo = RtlAllocateHeap (RtlGetProcessHeap(), HEAP_ZERO_MEMORY, KeyInfoSize); if (KeyInfo == NULL) { NtClose (KeyHandle); return STATUS_INSUFFICIENT_RESOURCES; } 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_INSUFFICIENT_RESOURCES; } Status = NtQueryValueKey (KeyHandle, &ValueNameString, KeyValuePartialInformation, KeyInfo, KeyInfoSize, &ResultSize); } NtClose (KeyHandle); if (!NT_SUCCESS(Status)) { if (KeyInfo != NULL) { RtlFreeHeap (RtlGetProcessHeap(), 0, KeyInfo); } return Status; } if (KeyInfo->Type != Type) { RtlFreeHeap (RtlGetProcessHeap(), 0, KeyInfo); return STATUS_OBJECT_TYPE_MISMATCH; } ResultSize = BufferSize; if (ResultSize < KeyInfo->DataLength) { Status = STATUS_BUFFER_OVERFLOW; } else { ResultSize = KeyInfo->DataLength; } RtlCopyMemory (Buffer, &KeyInfo->Data, ResultSize); RtlFreeHeap (RtlGetProcessHeap(), 0, KeyInfo); if (ReturnedLength != NULL) { *ReturnedLength = ResultSize; } return Status; } PIMAGE_BASE_RELOCATION NTAPI LdrProcessRelocationBlock(IN ULONG_PTR Address, IN ULONG Count, IN PUSHORT TypeOffset, IN LONG_PTR Delta) { SHORT Offset; USHORT Type; USHORT i; PUSHORT ShortPtr; PULONG LongPtr; 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 (PIMAGE_BASE_RELOCATION)TypeOffset; } NTSTATUS NTAPI LdrLockLoaderLock(IN ULONG Flags, OUT PULONG Disposition OPTIONAL, OUT PULONG Cookie OPTIONAL) { NTSTATUS Status; BOOLEAN Ret; BOOLEAN CookieSet = FALSE; if ((Flags != 0x01) && (Flags != 0x02)) return STATUS_INVALID_PARAMETER_1; if (!Cookie) return STATUS_INVALID_PARAMETER_3; /* Set some defaults for failure while verifying params */ _SEH2_TRY { *Cookie = 0; CookieSet = TRUE; if (Disposition) *Disposition = 0; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { if (CookieSet) Status = STATUS_INVALID_PARAMETER_3; else Status = STATUS_INVALID_PARAMETER_2; } _SEH2_END; if (Flags == 0x01) { DPRINT1("Warning: Reporting errors with exception not supported yet!\n"); RtlEnterCriticalSection(NtCurrentPeb()->LoaderLock); Status = STATUS_SUCCESS; } else { if (!Disposition) return STATUS_INVALID_PARAMETER_2; Ret = RtlTryEnterCriticalSection(NtCurrentPeb()->LoaderLock); if (Ret) *Disposition = 0x01; else *Disposition = 0x02; Status = STATUS_SUCCESS; } /* FIXME: Cookie is based on part of the thread id */ *Cookie = (ULONG)NtCurrentTeb()->RealClientId.UniqueThread; return Status; } NTSTATUS NTAPI LdrUnlockLoaderLock(IN ULONG Flags, IN ULONG Cookie OPTIONAL) { if (Flags != 0x01) return STATUS_INVALID_PARAMETER_1; if (Cookie != (ULONG)NtCurrentTeb()->RealClientId.UniqueThread) return STATUS_INVALID_PARAMETER_2; RtlLeaveCriticalSection(NtCurrentPeb()->LoaderLock); return STATUS_SUCCESS; } BOOLEAN NTAPI LdrUnloadAlternateResourceModule(IN PVOID BaseAddress) { UNIMPLEMENTED; return FALSE; }