[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.
This commit is contained in:
Ratin Gao 2025-03-03 04:13:33 +08:00 committed by GitHub
parent 6988b4e2c4
commit ccf1e97aa1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 539 additions and 53 deletions

View file

@ -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()

View file

@ -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

View file

@ -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(

View file

@ -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)
{

View file

@ -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) &&

View file

@ -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)

View file

@ -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()

View file

@ -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

View file

@ -0,0 +1,3 @@
#include "ntdll_vista.h"
PVOID LdrpHeap;

View file

@ -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 <ratin@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;
}

View file

@ -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"

View file

@ -1,3 +1,6 @@
@ stdcall LdrRegisterDllNotification(long ptr ptr ptr)
@ stdcall LdrUnregisterDllNotification(ptr)
@ stdcall RtlInitializeConditionVariable(ptr)
@ stdcall RtlWakeConditionVariable(ptr)
@ stdcall RtlWakeAllConditionVariable(ptr)

View file

@ -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")

View file

@ -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 <ratin@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);
}

View file

@ -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)

View file

@ -0,0 +1,11 @@
#include <windows.h>
BOOL
WINAPI
DllMain(
_In_ HINSTANCE hinstDLL,
_In_ DWORD fdwReason,
_In_ LPVOID lpvReserved)
{
return TRUE;
}

View file

@ -1,2 +1,3 @@
101 10 "load_notifications.dll"
102 10 "empty_dll.dll"

View file

@ -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 },

View file

@ -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

View file

@ -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_LOADED_NOTIFICATION_ENTRY
typedef struct _LDR_DLL_UNLOADED_NOTIFICATION_DATA
{
LIST_ENTRY NotificationListEntry;
PLDR_DLL_LOADED_NOTIFICATION_CALLBACK Callback;
} LDR_DLL_LOADED_NOTIFICATION_ENTRY, *PLDR_DLL_LOADED_NOTIFICATION_ENTRY;
ULONG Flags;
PCUNICODE_STRING FullDllName;
PCUNICODE_STRING BaseDllName;
PVOID DllBase;
ULONG SizeOfImage;
} LDR_DLL_UNLOADED_NOTIFICATION_DATA, *PLDR_DLL_UNLOADED_NOTIFICATION_DATA;
typedef union _LDR_DLL_NOTIFICATION_DATA
{
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

View file

@ -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(

View file

@ -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