https://git.reactos.org/?p=reactos.git;a=commitdiff;h=ccf1e97aa1bd056b4d4b7ce6fae1a4b089c6c25b
commit ccf1e97aa1bd056b4d4b7ce6fae1a4b089c6c25b Author: Ratin Gao <ra...@knsoft.org> AuthorDate: Mon Mar 3 04:13:33 2025 +0800 Commit: GitHub <nore...@github.com> CommitDate: Sun Mar 2 21:13:33 2025 +0100 [NTDLL_VISTA:LDR] Implement DLL Notification (#6795) Implement DLL Load Notification, an NT6+ feature. https://learn.microsoft.com/en-us/windows/win32/devnotes/dll-load-notification - [RTL] Sync `RTL_STATIC_LIST_HEAD` and `RtlFailFast` from XDK to NDK. - [NTDLL_VISTA] Introduce ntdll_vista_static static library and link both ntdll_vista and ntdll to it. - [NDK][LDR] Add and fix DLL Notification definitions. - [NTDLL_VISTA] Code improvements. - [NTDLL_VISTA:LDR] Implement Dll Notification. - [NTDLL][NTDLL_APITEST] Add Dll Notification API test. --- dll/ntdll/CMakeLists.txt | 2 +- dll/ntdll/def/ntdll.spec | 4 +- dll/ntdll/include/ntdllp.h | 14 ++ dll/ntdll/ldr/ldrapi.c | 8 +- dll/ntdll/ldr/ldrinit.c | 5 +- dll/ntdll/ldr/ldrutils.c | 7 +- dll/ntdll/nt_0600/CMakeLists.txt | 11 +- dll/ntdll/nt_0600/DllMain.c | 23 +- dll/ntdll/nt_0600/ldr/ldrinit.c | 3 + dll/ntdll/nt_0600/ldr/ldrnotify.c | 154 ++++++++++++++ dll/ntdll/nt_0600/ntdll_vista.h | 21 ++ dll/ntdll/nt_0600/ntdll_vista.spec | 3 + modules/rostests/apitests/ntdll/CMakeLists.txt | 6 +- .../rostests/apitests/ntdll/DllLoadNotification.c | 235 +++++++++++++++++++++ .../apitests/ntdll/empty_dll/CMakeLists.txt | 6 + .../rostests/apitests/ntdll/empty_dll/empty_dll.c | 11 + modules/rostests/apitests/ntdll/testdata.rc | 1 + modules/rostests/apitests/ntdll/testlist.c | 2 + sdk/include/ndk/ldrfuncs.h | 17 ++ sdk/include/ndk/ldrtypes.h | 39 +++- sdk/include/ndk/rtlfuncs.h | 12 ++ sdk/include/xdk/rtlfuncs.h | 6 +- 22 files changed, 538 insertions(+), 52 deletions(-) diff --git a/dll/ntdll/CMakeLists.txt b/dll/ntdll/CMakeLists.txt index fdd7c11262a..155ee21d4b5 100644 --- a/dll/ntdll/CMakeLists.txt +++ b/dll/ntdll/CMakeLists.txt @@ -65,7 +65,7 @@ set_module_type(ntdll win32dll ENTRYPOINT 0) set_subsystem(ntdll console) ################# END HACK ################# -target_link_libraries(ntdll etwtrace csrlib rtl rtl_um rtl_vista ntdllsys libcntpr uuid ${PSEH_LIB}) +target_link_libraries(ntdll ntdll_vista_static etwtrace csrlib rtl rtl_um rtl_vista ntdllsys libcntpr uuid ${PSEH_LIB}) if(DLL_EXPORT_VERSION GREATER_EQUAL 0x600) target_link_libraries(ntdll cryptlib) endif() diff --git a/dll/ntdll/def/ntdll.spec b/dll/ntdll/def/ntdll.spec index b023b06f9c3..de0d7f19516 100644 --- a/dll/ntdll/def/ntdll.spec +++ b/dll/ntdll/def/ntdll.spec @@ -170,7 +170,7 @@ @ stdcall LdrQueryImageFileKeyOption(ptr ptr long ptr long ptr) @ stdcall -stub -version=0x600+ LdrQueryModuleServiceTags(ptr ptr ptr) @ stdcall LdrQueryProcessModuleInformation(ptr long ptr) -@ stdcall -stub -version=0x600+ LdrRegisterDllNotification(long ptr ptr ptr) +@ stdcall -version=0x600+ LdrRegisterDllNotification(long ptr ptr ptr) @ stdcall -stub -version=0x600+ LdrRemoveLoadAsDataTable(ptr ptr ptr long) @ stub -version=0x600+ LdrResFindResource @ stub -version=0x600+ LdrResFindResourceDirectory @@ -185,7 +185,7 @@ @ stub -version=0x600+ LdrUnloadAlternateResourceModuleEx @ stdcall LdrUnloadDll(ptr) @ stdcall LdrUnlockLoaderLock(long ptr) -@ stdcall -stub -version=0x600+ LdrUnregisterDllNotification(ptr) +@ stdcall -version=0x600+ LdrUnregisterDllNotification(ptr) @ stdcall LdrVerifyImageMatchesChecksum(ptr long long long) @ stdcall -stub -version=0x600+ LdrVerifyImageMatchesChecksumEx(ptr ptr) @ stub -version=0x600+ LdrpResGetMappingSize diff --git a/dll/ntdll/include/ntdllp.h b/dll/ntdll/include/ntdllp.h index c5ea35a2761..bbef5444e86 100644 --- a/dll/ntdll/include/ntdllp.h +++ b/dll/ntdll/include/ntdllp.h @@ -229,6 +229,15 @@ VOID NTAPI LdrpFinalizeAndDeallocateDataTableEntry(IN PLDR_DATA_TABLE_ENTRY Entry); +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA) + +VOID +NTAPI +LdrpSendDllNotifications( + _In_ PLDR_DATA_TABLE_ENTRY DllEntry, + _In_ ULONG NotificationReason); + +#endif /* (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA) */ /* path.c */ BOOLEAN @@ -242,6 +251,11 @@ NTAPI RtlpInitializeKeyedEvent( VOID); +VOID +NTAPI +RtlpCloseKeyedEvent( + VOID); + VOID NTAPI RtlpInitializeThreadPooling( diff --git a/dll/ntdll/ldr/ldrapi.c b/dll/ntdll/ldr/ldrapi.c index a688385cc3a..8bb1f25ae13 100644 --- a/dll/ntdll/ldr/ldrapi.c +++ b/dll/ntdll/ldr/ldrapi.c @@ -1497,6 +1497,11 @@ LdrUnloadDll( DPRINT1("LDR: Unmapping [%ws]\n", LdrEntry->BaseDllName.Buffer); } +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA) + /* Send shutdown notification */ + LdrpSendDllNotifications(CurrentEntry, LDR_DLL_NOTIFICATION_REASON_UNLOADED); +#endif + /* Check if this is a .NET executable */ CorImageData = RtlImageDirectoryEntryToData(LdrEntry->DllBase, TRUE, @@ -1520,9 +1525,6 @@ LdrUnloadDll( /* Unload the alternate resource module, if any */ LdrUnloadAlternateResourceModule(CurrentEntry->DllBase); - /* FIXME: Send shutdown notification */ - //LdrpSendDllNotifications(CurrentEntry, 2, LdrpShutdownInProgress); - /* Check if a Hotpatch is active */ if (LdrEntry->PatchInformation) { diff --git a/dll/ntdll/ldr/ldrinit.c b/dll/ntdll/ldr/ldrinit.c index cde86383759..64831f660dd 100644 --- a/dll/ntdll/ldr/ldrinit.c +++ b/dll/ntdll/ldr/ldrinit.c @@ -56,9 +56,7 @@ ULONG LdrpNumberOfProcessors; PVOID NtDllBase; extern LARGE_INTEGER RtlpTimeout; extern BOOLEAN RtlpTimeoutDisable; -PVOID LdrpHeap; LIST_ENTRY LdrpHashTable[LDR_HASH_TABLE_ENTRIES]; -LIST_ENTRY LdrpDllNotificationList; HANDLE LdrpKnownDllObjectDirectory; UNICODE_STRING LdrpKnownDllPath; WCHAR LdrpKnownDllPathBuffer[128]; @@ -2008,9 +2006,8 @@ LdrpInitializeProcess(IN PCONTEXT Context, //Peb->FastPebLockRoutine = (PPEBLOCKROUTINE)RtlEnterCriticalSection; //Peb->FastPebUnlockRoutine = (PPEBLOCKROUTINE)RtlLeaveCriticalSection; - /* Setup Callout Lock and Notification list */ + /* Setup Callout Lock */ //RtlInitializeCriticalSection(&RtlpCalloutEntryLock); - InitializeListHead(&LdrpDllNotificationList); /* For old executables, use 16-byte aligned heap */ if ((NtHeader->OptionalHeader.MajorSubsystemVersion <= 3) && diff --git a/dll/ntdll/ldr/ldrutils.c b/dll/ntdll/ldr/ldrutils.c index e46bb3446dd..19c90865531 100644 --- a/dll/ntdll/ldr/ldrutils.c +++ b/dll/ntdll/ldr/ldrutils.c @@ -1226,7 +1226,12 @@ SkipCheck: /* Insert this entry */ LdrpInsertMemoryTableEntry(LdrEntry); - // LdrpSendDllNotifications(LdrEntry, TRUE, Status == STATUS_IMAGE_NOT_AT_BASE) +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA) + LdrpSendDllNotifications(LdrEntry, LDR_DLL_NOTIFICATION_REASON_LOADED); +#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8) + LdrEntry->Flags |= LDRP_LOAD_NOTIFICATIONS_SENT; /* LdrEntry->LoadNotificationsSent = TRUE; */ +#endif +#endif /* Check for invalid CPU Image */ if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH) diff --git a/dll/ntdll/nt_0600/CMakeLists.txt b/dll/ntdll/nt_0600/CMakeLists.txt index 2eec0df9940..69b9b5f0ff8 100644 --- a/dll/ntdll/nt_0600/CMakeLists.txt +++ b/dll/ntdll/nt_0600/CMakeLists.txt @@ -11,12 +11,15 @@ include_directories( ${REACTOS_SOURCE_DIR}/sdk/include/reactos/subsys) list(APPEND SOURCE - DllMain.c - ${CMAKE_CURRENT_BINARY_DIR}/ntdll_vista.def) + ldr/ldrinit.c + ldr/ldrnotify.c) -add_library(ntdll_vista MODULE ${SOURCE} ntdll_vista.rc) +add_library(ntdll_vista_static ${SOURCE}) +target_link_libraries(ntdll_vista_static) +add_dependencies(ntdll_vista_static psdk) +add_library(ntdll_vista MODULE DllMain.c ${CMAKE_CURRENT_BINARY_DIR}/ntdll_vista.def ntdll_vista.rc) set_module_type(ntdll_vista win32dll ENTRYPOINT DllMain 12) -target_link_libraries(ntdll_vista smlib rtl_vista) +target_link_libraries(ntdll_vista ntdll_vista_static smlib rtl_vista ${PSEH_LIB}) if(ARCH STREQUAL "arm") target_link_libraries(ntdll_vista chkstk) endif() diff --git a/dll/ntdll/nt_0600/DllMain.c b/dll/ntdll/nt_0600/DllMain.c index 90a01419b97..38a0d448916 100644 --- a/dll/ntdll/nt_0600/DllMain.c +++ b/dll/ntdll/nt_0600/DllMain.c @@ -1,25 +1,4 @@ -#include <stdarg.h> - -#define WIN32_NO_STATUS - -#include <windef.h> -#include <winbase.h> -#include <winreg.h> -#include <winuser.h> -#include <winwlx.h> -#include <ndk/rtltypes.h> -#include <ndk/umfuncs.h> - -#define NDEBUG -#include <debug.h> - -VOID -NTAPI -RtlpInitializeKeyedEvent(VOID); - -VOID -NTAPI -RtlpCloseKeyedEvent(VOID); +#include "ntdll_vista.h" BOOL WINAPI diff --git a/dll/ntdll/nt_0600/ldr/ldrinit.c b/dll/ntdll/nt_0600/ldr/ldrinit.c new file mode 100644 index 00000000000..e9251d9ebf5 --- /dev/null +++ b/dll/ntdll/nt_0600/ldr/ldrinit.c @@ -0,0 +1,3 @@ +#include "ntdll_vista.h" + +PVOID LdrpHeap; diff --git a/dll/ntdll/nt_0600/ldr/ldrnotify.c b/dll/ntdll/nt_0600/ldr/ldrnotify.c new file mode 100644 index 00000000000..70ae996c131 --- /dev/null +++ b/dll/ntdll/nt_0600/ldr/ldrnotify.c @@ -0,0 +1,154 @@ +/* + * PROJECT: ReactOS NT Layer/System API + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: DLL Load Notification Implementation + * COPYRIGHT: Copyright 2024 Ratin Gao <ra...@knsoft.org> + */ + +#include "ntdll_vista.h" + +/* GLOBALS *******************************************************************/ + +typedef struct _LDR_DLL_NOTIFICATION_ENTRY +{ + LIST_ENTRY List; + PLDR_DLL_NOTIFICATION_FUNCTION Callback; + PVOID Context; +} LDR_DLL_NOTIFICATION_ENTRY, *PLDR_DLL_NOTIFICATION_ENTRY; + +static RTL_STATIC_LIST_HEAD(LdrpDllNotificationList); + +/* Initialize critical section statically */ +static RTL_CRITICAL_SECTION LdrpDllNotificationLock; +static RTL_CRITICAL_SECTION_DEBUG LdrpDllNotificationLockDebug = { + .CriticalSection = &LdrpDllNotificationLock +}; +static RTL_CRITICAL_SECTION LdrpDllNotificationLock = { + &LdrpDllNotificationLockDebug, + -1, + 0, + 0, + 0, + 0 +}; + +/* FUNCTIONS *****************************************************************/ + +NTSTATUS +NTAPI +LdrRegisterDllNotification( + _In_ ULONG Flags, + _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, + _In_opt_ PVOID Context, + _Out_ PVOID *Cookie) +{ + PLDR_DLL_NOTIFICATION_ENTRY NewEntry; + + /* Check input parameters */ + if (Flags != 0 || NotificationFunction == NULL || Cookie == NULL) + { + return STATUS_INVALID_PARAMETER; + } + + /* Allocate new entry and assign input values */ + NewEntry = RtlAllocateHeap(LdrpHeap, 0, sizeof(*NewEntry)); + if (NewEntry == NULL) + { + return STATUS_NO_MEMORY; + } + NewEntry->Callback = NotificationFunction; + NewEntry->Context = Context; + + /* Add node to the end of global list */ + RtlEnterCriticalSection(&LdrpDllNotificationLock); + InsertTailList(&LdrpDllNotificationList, &NewEntry->List); + RtlLeaveCriticalSection(&LdrpDllNotificationLock); + + /* Cookie is address of the new entry */ + *Cookie = NewEntry; + return STATUS_SUCCESS; +} + +NTSTATUS +NTAPI +LdrUnregisterDllNotification( + _In_ PVOID Cookie) +{ + NTSTATUS Status = STATUS_DLL_NOT_FOUND; + PLIST_ENTRY Entry; + + /* Find entry to remove */ + RtlEnterCriticalSection(&LdrpDllNotificationLock); + for (Entry = LdrpDllNotificationList.Flink; + Entry != &LdrpDllNotificationList; + Entry = Entry->Flink) + { + if (Entry == Cookie) + { + RemoveEntryList(Entry); + Status = STATUS_SUCCESS; + break; + } + } + RtlLeaveCriticalSection(&LdrpDllNotificationLock); + + if (NT_SUCCESS(Status)) + { + RtlFreeHeap(LdrpHeap, 0, Entry); + } + return Status; +} + +VOID +NTAPI +LdrpSendDllNotifications( + _In_ PLDR_DATA_TABLE_ENTRY DllEntry, + _In_ ULONG NotificationReason) +{ + PLIST_ENTRY Entry; + PLDR_DLL_NOTIFICATION_ENTRY NotificationEntry; + LDR_DLL_NOTIFICATION_DATA NotificationData; + + /* + * LDR_DLL_LOADED_NOTIFICATION_DATA and LDR_DLL_UNLOADED_NOTIFICATION_DATA + * currently are the same. Use C_ASSERT to ensure it, then fill either of them. + */ +#define LdrpAssertDllNotificationDataMember(x)\ + C_ASSERT(FIELD_OFFSET(LDR_DLL_NOTIFICATION_DATA, Loaded.x) ==\ + FIELD_OFFSET(LDR_DLL_NOTIFICATION_DATA, Unloaded.x)) + + C_ASSERT(sizeof(NotificationData.Loaded) == sizeof(NotificationData.Unloaded)); + LdrpAssertDllNotificationDataMember(Flags); + LdrpAssertDllNotificationDataMember(FullDllName); + LdrpAssertDllNotificationDataMember(BaseDllName); + LdrpAssertDllNotificationDataMember(DllBase); + LdrpAssertDllNotificationDataMember(SizeOfImage); + +#undef LdrpAssertDllNotificationDataMember + + NotificationData.Loaded.Flags = 0; /* Reserved and always 0, not DllEntry->Flags */ + NotificationData.Loaded.FullDllName = &DllEntry->FullDllName; + NotificationData.Loaded.BaseDllName = &DllEntry->BaseDllName; + NotificationData.Loaded.DllBase = DllEntry->DllBase; + NotificationData.Loaded.SizeOfImage = DllEntry->SizeOfImage; + + /* Send notification to all registered callbacks */ + RtlEnterCriticalSection(&LdrpDllNotificationLock); + _SEH2_TRY + { + for (Entry = LdrpDllNotificationList.Flink; + Entry != &LdrpDllNotificationList; + Entry = Entry->Flink) + { + NotificationEntry = CONTAINING_RECORD(Entry, LDR_DLL_NOTIFICATION_ENTRY, List); + NotificationEntry->Callback(NotificationReason, + &NotificationData, + NotificationEntry->Context); + } + } + _SEH2_FINALLY + { + RtlLeaveCriticalSection(&LdrpDllNotificationLock); + } + _SEH2_END; +} diff --git a/dll/ntdll/nt_0600/ntdll_vista.h b/dll/ntdll/nt_0600/ntdll_vista.h new file mode 100644 index 00000000000..47051576c5c --- /dev/null +++ b/dll/ntdll/nt_0600/ntdll_vista.h @@ -0,0 +1,21 @@ +#pragma once + +#define _NTSYSTEM_ +#define _NTDLLBUILD_ + +#include <stdarg.h> + +#define WIN32_NO_STATUS +#include <windef.h> +#include <winbase.h> +#include <winnt.h> + +#define NTOS_MODE_USER +#include <ndk/rtlfuncs.h> +#include <ndk/umfuncs.h> +#include <ndk/ldrfuncs.h> + +#define NDEBUG +#include <debug.h> + +#include "../include/ntdllp.h" diff --git a/dll/ntdll/nt_0600/ntdll_vista.spec b/dll/ntdll/nt_0600/ntdll_vista.spec index d36c6449d8e..2bf9f747a00 100644 --- a/dll/ntdll/nt_0600/ntdll_vista.spec +++ b/dll/ntdll/nt_0600/ntdll_vista.spec @@ -1,3 +1,6 @@ +@ stdcall LdrRegisterDllNotification(long ptr ptr ptr) +@ stdcall LdrUnregisterDllNotification(ptr) + @ stdcall RtlInitializeConditionVariable(ptr) @ stdcall RtlWakeConditionVariable(ptr) @ stdcall RtlWakeAllConditionVariable(ptr) diff --git a/modules/rostests/apitests/ntdll/CMakeLists.txt b/modules/rostests/apitests/ntdll/CMakeLists.txt index 29c1cf96ed9..e371e95305f 100644 --- a/modules/rostests/apitests/ntdll/CMakeLists.txt +++ b/modules/rostests/apitests/ntdll/CMakeLists.txt @@ -1,11 +1,14 @@ add_subdirectory(load_notifications) +add_subdirectory(empty_dll) include_directories($<TARGET_FILE_DIR:load_notifications>) +include_directories($<TARGET_FILE_DIR:empty_dll>) include_directories(${REACTOS_SOURCE_DIR}/ntoskrnl/include) spec2def(ntdll_apitest.exe ntdll_apitest.spec) list(APPEND SOURCE + DllLoadNotification.c LdrEnumResources.c LdrLoadDll.c load_notifications.c @@ -118,6 +121,7 @@ list(APPEND PCH_SKIP_SOURCE testlist.c) add_rc_deps(testdata.rc ${CMAKE_CURRENT_BINARY_DIR}/load_notifications/load_notifications.dll) +add_rc_deps(testdata.rc ${CMAKE_CURRENT_BINARY_DIR}/empty_dll/empty_dll.dll) add_executable(ntdll_apitest ${SOURCE} @@ -135,7 +139,7 @@ target_link_libraries(ntdll_apitest rtl_test_lib wine uuid ${PSEH_LIB}) set_module_type(ntdll_apitest win32cui) add_importlibs(ntdll_apitest msvcrt advapi32 kernel32 ntdll) add_pch(ntdll_apitest precomp.h "${PCH_SKIP_SOURCE}") -add_dependencies(ntdll_apitest load_notifications) +add_dependencies(ntdll_apitest load_notifications empty_dll) if(NOT MSVC) set_source_files_properties(RtlGetFullPathName_UstrEx.c PROPERTIES COMPILE_OPTIONS "-Wno-format") diff --git a/modules/rostests/apitests/ntdll/DllLoadNotification.c b/modules/rostests/apitests/ntdll/DllLoadNotification.c new file mode 100644 index 00000000000..30ed50e38a3 --- /dev/null +++ b/modules/rostests/apitests/ntdll/DllLoadNotification.c @@ -0,0 +1,235 @@ +/* + * PROJECT: ReactOS API Tests + * LICENSE: MIT (https://spdx.org/licenses/MIT) + * PURPOSE: Test for DLL Load Notification API + * COPYRIGHT: Copyright 2024 Ratin Gao <ra...@knsoft.org> + */ + +#define UNICODE + +#include "precomp.h" + +#include <winuser.h> + +static WCHAR g_szDllPath[MAX_PATH]; +static UNICODE_STRING g_usDllPath; +static UNICODE_STRING g_usDllName; +static volatile LONG g_lDllLoadCount = 0; + +typedef +NTSTATUS +NTAPI +FN_LdrRegisterDllNotification( + _In_ ULONG Flags, + _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, + _In_opt_ PVOID Context, + _Out_ PVOID* Cookie); + +typedef +NTSTATUS +NTAPI +FN_LdrUnregisterDllNotification( + _In_ PVOID Cookie); + +static BOOL ExtractResource( + _In_z_ PCWSTR SavePath, + _In_ PCWSTR ResourceType, + _In_ PCWSTR ResourceName) +{ + BOOL bSuccess; + DWORD dwWritten, dwSize; + HGLOBAL hGlobal; + LPVOID pData; + HANDLE hFile; + HRSRC hRsrc; + + /* Load resource */ + if ((hRsrc = FindResourceW(NULL, ResourceName, ResourceType)) == NULL || + (dwSize = SizeofResource(NULL, hRsrc)) == 0 || + (hGlobal = LoadResource(NULL, hRsrc)) == NULL || + (pData = LockResource(hGlobal)) == NULL) + { + return FALSE; + } + + /* Save to file */ + hFile = CreateFileW(SavePath, + GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (hFile == INVALID_HANDLE_VALUE) + { + return FALSE; + } + bSuccess = WriteFile(hFile, pData, dwSize, &dwWritten, NULL); + CloseHandle(hFile); + if (!bSuccess) + { + return FALSE; + } + else if (dwWritten != dwSize) + { + trace("Extract resource failed, written size (%lu) is not actual size (%lu)\n", dwWritten, dwSize); + DeleteFileW(SavePath); + SetLastError(ERROR_INCORRECT_SIZE); + return FALSE; + } + return TRUE; +} + +static VOID NTAPI DllLoadCallback( + _In_ ULONG NotificationReason, + _In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData, + _In_opt_ PVOID Context) +{ + LONG lRet; + HMODULE* phNotifiedDllBase = Context; + + /* + * Verify the data, + * NotificationData->Loaded and NotificationData->Unloaded currently are the same. + */ + + /* Verify the FullDllName and BaseDllName */ + ok_eq_ulong(NotificationData->Loaded.Flags, 0UL); + lRet = RtlCompareUnicodeString(NotificationData->Loaded.FullDllName, + (PCUNICODE_STRING)&g_usDllPath, + TRUE); + ok_eq_long(lRet, 0L); + lRet = RtlCompareUnicodeString(NotificationData->Loaded.BaseDllName, + (PCUNICODE_STRING)&g_usDllName, + TRUE); + ok_eq_long(lRet, 0L); + + /* + * Verify SizeOfImage and read SizeOfImage from PE header, + * make sure the DLL is not unmapped, the memory is still accessible. + */ + ok_eq_ulong(NotificationData->Loaded.SizeOfImage, + RtlImageNtHeader(NotificationData->Loaded.DllBase)->OptionalHeader.SizeOfImage); + + /* Reason can be load or unload */ + ok(NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED || + NotificationReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED, "Incorrect NotificationReason\n"); + if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_LOADED) + { + *phNotifiedDllBase = NotificationData->Loaded.DllBase; + InterlockedIncrement(&g_lDllLoadCount); + } + else if (NotificationReason == LDR_DLL_NOTIFICATION_REASON_UNLOADED) + { + InterlockedDecrement(&g_lDllLoadCount); + } +} + +START_TEST(DllLoadNotification) +{ + WCHAR szTempPath[MAX_PATH]; + PCWSTR pszDllName; + HMODULE hNtDll, hTestDll, hNotifiedDllBase; + FN_LdrRegisterDllNotification* pfnLdrRegisterDllNotification; + FN_LdrUnregisterDllNotification* pfnLdrUnregisterDllNotification; + NTSTATUS Status; + PVOID Cookie1, Cookie2; + + /* Load functions */ + hNtDll = GetModuleHandleW(L"ntdll.dll"); + if (hNtDll == NULL) + { + skip("GetModuleHandleW for ntdll failed with 0x%08lX\n", GetLastError()); + return; + } + pfnLdrRegisterDllNotification = (FN_LdrRegisterDllNotification*)GetProcAddress(hNtDll, "LdrRegisterDllNotification"); + pfnLdrUnregisterDllNotification = (FN_LdrUnregisterDllNotification*)GetProcAddress(hNtDll, "LdrUnregisterDllNotification"); + if (!pfnLdrRegisterDllNotification || !pfnLdrUnregisterDllNotification) + { + skip("ntdll.dll!Ldr[Un]RegisterDllNotification not found\n"); + return; + } + + /* Extract DLL to temp directory */ + if (!GetTempPathW(ARRAYSIZE(szTempPath), szTempPath)) + { + skip("GetTempPathW failed with 0x%08lX\n", GetLastError()); + return; + } + if (GetTempFileNameW(szTempPath, L"DLN", 0, g_szDllPath) == 0) + { + skip("GetTempFileNameW failed with 0x%08lX\n", GetLastError()); + return; + } + RtlInitUnicodeString(&g_usDllPath, g_szDllPath); + pszDllName = wcsrchr(g_szDllPath, L'\\') + 1; + if (pszDllName == NULL) + { + skip("Find file name of %ls failed\n", g_szDllPath); + return; + } + RtlInitUnicodeString(&g_usDllName, pszDllName); + if (!ExtractResource(g_szDllPath, RT_RCDATA, MAKEINTRESOURCEW(102))) + { + skip("ExtractResource failed with 0x%08lX\n", GetLastError()); + return; + } + + /* Register DLL load notification callback */ + hNotifiedDllBase = NULL; + Cookie1 = NULL; + Cookie2 = NULL; + Status = pfnLdrRegisterDllNotification(0, DllLoadCallback, &hNotifiedDllBase, &Cookie1); + ok_eq_bool(NT_SUCCESS(Status), TRUE); + ok(Cookie1 != NULL, "Cookie1 is NULL\n"); + + /* Register the callback again is valid */ + Status = pfnLdrRegisterDllNotification(0, DllLoadCallback, &hNotifiedDllBase, &Cookie2); + ok_eq_bool(NT_SUCCESS(Status), TRUE); + ok(Cookie2 != NULL, "Cookie2 is NULL\n"); + + /* Load the test DLL */ + hTestDll = LoadLibraryW(g_szDllPath); + if (!hTestDll) + { + skip("LoadLibraryW failed with 0x%08lX\n", GetLastError()); + goto _exit; + } + + /* Verify the Dll base received in callback and returned via context */ + ok_eq_pointer(hNotifiedDllBase, hTestDll); + + /* The count should be 2 because the callback was registered twice */ + ok_eq_long(g_lDllLoadCount, 2L); + + /* + * Callback will not be triggered because following + * load and unload actions change the DLL reference count only + */ + LoadLibraryW(g_szDllPath); + ok_eq_long(g_lDllLoadCount, 2L); + FreeLibrary(hTestDll); + ok_eq_long(g_lDllLoadCount, 2L); + + /* Unregister the callback once */ + Status = pfnLdrUnregisterDllNotification(Cookie1); + ok_eq_bool(NT_SUCCESS(Status), TRUE); + + /* Unload the test DLL */ + if (FreeLibrary(hTestDll)) + { + /* The count will decrease 1 because the last callback still there */ + ok_eq_long(g_lDllLoadCount, 1L); + } + else + { + skip("FreeLibrary failed with 0x%08lX\n", GetLastError()); + } + + /* Unregister the last callback */ + Status = pfnLdrUnregisterDllNotification(Cookie2); + ok_eq_bool(NT_SUCCESS(Status), TRUE); + +_exit: + DeleteFileW(g_szDllPath); +} diff --git a/modules/rostests/apitests/ntdll/empty_dll/CMakeLists.txt b/modules/rostests/apitests/ntdll/empty_dll/CMakeLists.txt new file mode 100644 index 00000000000..36513ed6703 --- /dev/null +++ b/modules/rostests/apitests/ntdll/empty_dll/CMakeLists.txt @@ -0,0 +1,6 @@ + +add_library(empty_dll MODULE empty_dll.c) +set_module_type(empty_dll win32dll ENTRYPOINT DllMain 12) +add_importlibs(empty_dll kernel32 ntdll) +add_dependencies(empty_dll psdk) +add_rostests_file(TARGET empty_dll) diff --git a/modules/rostests/apitests/ntdll/empty_dll/empty_dll.c b/modules/rostests/apitests/ntdll/empty_dll/empty_dll.c new file mode 100644 index 00000000000..ddd4be786a8 --- /dev/null +++ b/modules/rostests/apitests/ntdll/empty_dll/empty_dll.c @@ -0,0 +1,11 @@ +#include <windows.h> + +BOOL +WINAPI +DllMain( + _In_ HINSTANCE hinstDLL, + _In_ DWORD fdwReason, + _In_ LPVOID lpvReserved) +{ + return TRUE; +} diff --git a/modules/rostests/apitests/ntdll/testdata.rc b/modules/rostests/apitests/ntdll/testdata.rc index 3c16769902c..3937c7302ef 100644 --- a/modules/rostests/apitests/ntdll/testdata.rc +++ b/modules/rostests/apitests/ntdll/testdata.rc @@ -1,2 +1,3 @@ 101 10 "load_notifications.dll" +102 10 "empty_dll.dll" diff --git a/modules/rostests/apitests/ntdll/testlist.c b/modules/rostests/apitests/ntdll/testlist.c index 86f653d5adf..c4da00ff0c0 100644 --- a/modules/rostests/apitests/ntdll/testlist.c +++ b/modules/rostests/apitests/ntdll/testlist.c @@ -3,6 +3,7 @@ #define STANDALONE #include <apitest.h> +extern void func_DllLoadNotification(void); extern void func_LdrEnumResources(void); extern void func_LdrLoadDll(void); extern void func_load_notifications(void); @@ -107,6 +108,7 @@ extern void func_UserModeException(void); const struct test winetest_testlist[] = { + { "DllLoadNotification", func_DllLoadNotification }, { "LdrEnumResources", func_LdrEnumResources }, { "LdrLoadDll", func_LdrLoadDll }, { "load_notifications", func_load_notifications }, diff --git a/sdk/include/ndk/ldrfuncs.h b/sdk/include/ndk/ldrfuncs.h index 62e334aacf7..bdade6abcf9 100644 --- a/sdk/include/ndk/ldrfuncs.h +++ b/sdk/include/ndk/ldrfuncs.h @@ -147,6 +147,23 @@ LdrEnumerateLoadedModules( _In_opt_ PVOID Context ); +#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA) + +NTSTATUS +NTAPI +LdrRegisterDllNotification( + _In_ ULONG Flags, + _In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction, + _In_opt_ PVOID Context, + _Out_ PVOID* Cookie); + +NTSTATUS +NTAPI +LdrUnregisterDllNotification( + _In_ PVOID Cookie); + +#endif /* (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA) */ + #ifdef NTOS_MODE_USER NTSYSAPI BOOLEAN diff --git a/sdk/include/ndk/ldrtypes.h b/sdk/include/ndk/ldrtypes.h index 3b71d022887..4c16bd524c1 100644 --- a/sdk/include/ndk/ldrtypes.h +++ b/sdk/include/ndk/ldrtypes.h @@ -37,7 +37,11 @@ Author: // #define LDRP_STATIC_LINK 0x00000002 #define LDRP_IMAGE_DLL 0x00000004 +#if (NTDDI_VERSION < NTDDI_WIN8) #define LDRP_SHIMENG_SUPPRESSED_ENTRY 0x00000008 +#else +#define LDRP_LOAD_NOTIFICATIONS_SENT 0x00000008 +#endif #define LDRP_IMAGE_INTEGRITY_FORCED 0x00000020 #define LDRP_LOAD_IN_PROGRESS 0x00001000 #define LDRP_UNLOAD_IN_PROGRESS 0x00002000 @@ -196,26 +200,39 @@ typedef struct _LDR_ENUM_RESOURCE_INFO // // DLL Notifications // +#define LDR_DLL_NOTIFICATION_REASON_LOADED 1 +#define LDR_DLL_NOTIFICATION_REASON_UNLOADED 2 + typedef struct _LDR_DLL_LOADED_NOTIFICATION_DATA { ULONG Flags; - PUNICODE_STRING FullDllName; - PUNICODE_STRING BaseDllName; + PCUNICODE_STRING FullDllName; + PCUNICODE_STRING BaseDllName; PVOID DllBase; ULONG SizeOfImage; } LDR_DLL_LOADED_NOTIFICATION_DATA, *PLDR_DLL_LOADED_NOTIFICATION_DATA; -typedef VOID -(NTAPI *PLDR_DLL_LOADED_NOTIFICATION_CALLBACK)( - _In_ BOOLEAN Type, - _In_ struct _LDR_DLL_LOADED_NOTIFICATION_DATA *Data -); +typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA +{ + ULONG Flags; + PCUNICODE_STRING FullDllName; + PCUNICODE_STRING BaseDllName; + PVOID DllBase; + ULONG SizeOfImage; +} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA; -typedef struct _LDR_DLL_LOADED_NOTIFICATION_ENTRY +typedef union _LDR_DLL_NOTIFICATION_DATA { - LIST_ENTRY NotificationListEntry; - PLDR_DLL_LOADED_NOTIFICATION_CALLBACK Callback; -} LDR_DLL_LOADED_NOTIFICATION_ENTRY, *PLDR_DLL_LOADED_NOTIFICATION_ENTRY; + LDR_DLL_LOADED_NOTIFICATION_DATA Loaded; + LDR_DLL_UNLOADED_NOTIFICATION_DATA Unloaded; +} LDR_DLL_NOTIFICATION_DATA, *PLDR_DLL_NOTIFICATION_DATA; +typedef const LDR_DLL_NOTIFICATION_DATA *PCLDR_DLL_NOTIFICATION_DATA; + +typedef VOID +(NTAPI *PLDR_DLL_NOTIFICATION_FUNCTION)( + _In_ ULONG NotificationReason, + _In_ PCLDR_DLL_NOTIFICATION_DATA NotificationData, + _In_opt_ PVOID Context); // // Alternate Resources Support diff --git a/sdk/include/ndk/rtlfuncs.h b/sdk/include/ndk/rtlfuncs.h index c9135cfc909..f506e595c18 100644 --- a/sdk/include/ndk/rtlfuncs.h +++ b/sdk/include/ndk/rtlfuncs.h @@ -39,6 +39,18 @@ extern "C" { // // List Functions // + +DECLSPEC_NORETURN +FORCEINLINE +VOID +RtlFailFast( + _In_ ULONG Code) +{ + __fastfail(Code); +} + +#define RTL_STATIC_LIST_HEAD(x) LIST_ENTRY x = { &x, &x } + FORCEINLINE VOID InitializeListHead( diff --git a/sdk/include/xdk/rtlfuncs.h b/sdk/include/xdk/rtlfuncs.h index 6aeca552d6e..f35b8e28452 100644 --- a/sdk/include/xdk/rtlfuncs.h +++ b/sdk/include/xdk/rtlfuncs.h @@ -22,6 +22,9 @@ $if (_WDMDDK_ || _WINNT_) #define FAST_FAIL_MRDATA_MODIFIED 19 #define FAST_FAIL_INVALID_FAST_FAIL_CODE 0xFFFFFFFF +$endif(_WDMDDK_ || _WINNT_) +$if (_WDMDDK_) + DECLSPEC_NORETURN FORCEINLINE VOID @@ -31,9 +34,6 @@ RtlFailFast( __fastfail(Code); } -$endif(_WDMDDK_ || _WINNT_) -$if (_WDMDDK_) - #if !defined(NO_KERNEL_LIST_ENTRY_CHECKS) && (defined(_M_CEE_PURE) || defined(_M_CEE_SAFE)) #define NO_KERNEL_LIST_ENTRY_CHECKS #endif