Skip to content

Commit ccf1e97

Browse files
authored
[NTDLL_VISTA:LDR] Implement DLL Notification (reactos#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.
1 parent 6988b4e commit ccf1e97

File tree

22 files changed

+538
-52
lines changed

22 files changed

+538
-52
lines changed

dll/ntdll/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ set_module_type(ntdll win32dll ENTRYPOINT 0)
6565
set_subsystem(ntdll console)
6666
################# END HACK #################
6767

68-
target_link_libraries(ntdll etwtrace csrlib rtl rtl_um rtl_vista ntdllsys libcntpr uuid ${PSEH_LIB})
68+
target_link_libraries(ntdll ntdll_vista_static etwtrace csrlib rtl rtl_um rtl_vista ntdllsys libcntpr uuid ${PSEH_LIB})
6969
if(DLL_EXPORT_VERSION GREATER_EQUAL 0x600)
7070
target_link_libraries(ntdll cryptlib)
7171
endif()

dll/ntdll/def/ntdll.spec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@
170170
@ stdcall LdrQueryImageFileKeyOption(ptr ptr long ptr long ptr)
171171
@ stdcall -stub -version=0x600+ LdrQueryModuleServiceTags(ptr ptr ptr)
172172
@ stdcall LdrQueryProcessModuleInformation(ptr long ptr)
173-
@ stdcall -stub -version=0x600+ LdrRegisterDllNotification(long ptr ptr ptr)
173+
@ stdcall -version=0x600+ LdrRegisterDllNotification(long ptr ptr ptr)
174174
@ stdcall -stub -version=0x600+ LdrRemoveLoadAsDataTable(ptr ptr ptr long)
175175
@ stub -version=0x600+ LdrResFindResource
176176
@ stub -version=0x600+ LdrResFindResourceDirectory
@@ -185,7 +185,7 @@
185185
@ stub -version=0x600+ LdrUnloadAlternateResourceModuleEx
186186
@ stdcall LdrUnloadDll(ptr)
187187
@ stdcall LdrUnlockLoaderLock(long ptr)
188-
@ stdcall -stub -version=0x600+ LdrUnregisterDllNotification(ptr)
188+
@ stdcall -version=0x600+ LdrUnregisterDllNotification(ptr)
189189
@ stdcall LdrVerifyImageMatchesChecksum(ptr long long long)
190190
@ stdcall -stub -version=0x600+ LdrVerifyImageMatchesChecksumEx(ptr ptr)
191191
@ stub -version=0x600+ LdrpResGetMappingSize

dll/ntdll/include/ntdllp.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,15 @@ VOID
229229
NTAPI
230230
LdrpFinalizeAndDeallocateDataTableEntry(IN PLDR_DATA_TABLE_ENTRY Entry);
231231

232+
#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA)
233+
234+
VOID
235+
NTAPI
236+
LdrpSendDllNotifications(
237+
_In_ PLDR_DATA_TABLE_ENTRY DllEntry,
238+
_In_ ULONG NotificationReason);
239+
240+
#endif /* (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA) */
232241

233242
/* path.c */
234243
BOOLEAN
@@ -242,6 +251,11 @@ NTAPI
242251
RtlpInitializeKeyedEvent(
243252
VOID);
244253

254+
VOID
255+
NTAPI
256+
RtlpCloseKeyedEvent(
257+
VOID);
258+
245259
VOID
246260
NTAPI
247261
RtlpInitializeThreadPooling(

dll/ntdll/ldr/ldrapi.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1497,6 +1497,11 @@ LdrUnloadDll(
14971497
DPRINT1("LDR: Unmapping [%ws]\n", LdrEntry->BaseDllName.Buffer);
14981498
}
14991499

1500+
#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA)
1501+
/* Send shutdown notification */
1502+
LdrpSendDllNotifications(CurrentEntry, LDR_DLL_NOTIFICATION_REASON_UNLOADED);
1503+
#endif
1504+
15001505
/* Check if this is a .NET executable */
15011506
CorImageData = RtlImageDirectoryEntryToData(LdrEntry->DllBase,
15021507
TRUE,
@@ -1520,9 +1525,6 @@ LdrUnloadDll(
15201525
/* Unload the alternate resource module, if any */
15211526
LdrUnloadAlternateResourceModule(CurrentEntry->DllBase);
15221527

1523-
/* FIXME: Send shutdown notification */
1524-
//LdrpSendDllNotifications(CurrentEntry, 2, LdrpShutdownInProgress);
1525-
15261528
/* Check if a Hotpatch is active */
15271529
if (LdrEntry->PatchInformation)
15281530
{

dll/ntdll/ldr/ldrinit.c

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,7 @@ ULONG LdrpNumberOfProcessors;
5656
PVOID NtDllBase;
5757
extern LARGE_INTEGER RtlpTimeout;
5858
extern BOOLEAN RtlpTimeoutDisable;
59-
PVOID LdrpHeap;
6059
LIST_ENTRY LdrpHashTable[LDR_HASH_TABLE_ENTRIES];
61-
LIST_ENTRY LdrpDllNotificationList;
6260
HANDLE LdrpKnownDllObjectDirectory;
6361
UNICODE_STRING LdrpKnownDllPath;
6462
WCHAR LdrpKnownDllPathBuffer[128];
@@ -2008,9 +2006,8 @@ LdrpInitializeProcess(IN PCONTEXT Context,
20082006
//Peb->FastPebLockRoutine = (PPEBLOCKROUTINE)RtlEnterCriticalSection;
20092007
//Peb->FastPebUnlockRoutine = (PPEBLOCKROUTINE)RtlLeaveCriticalSection;
20102008

2011-
/* Setup Callout Lock and Notification list */
2009+
/* Setup Callout Lock */
20122010
//RtlInitializeCriticalSection(&RtlpCalloutEntryLock);
2013-
InitializeListHead(&LdrpDllNotificationList);
20142011

20152012
/* For old executables, use 16-byte aligned heap */
20162013
if ((NtHeader->OptionalHeader.MajorSubsystemVersion <= 3) &&

dll/ntdll/ldr/ldrutils.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1226,7 +1226,12 @@ LdrpMapDll(IN PWSTR SearchPath OPTIONAL,
12261226
/* Insert this entry */
12271227
LdrpInsertMemoryTableEntry(LdrEntry);
12281228

1229-
// LdrpSendDllNotifications(LdrEntry, TRUE, Status == STATUS_IMAGE_NOT_AT_BASE)
1229+
#if (_WIN32_WINNT >= _WIN32_WINNT_VISTA) || (DLL_EXPORT_VERSION >= _WIN32_WINNT_VISTA)
1230+
LdrpSendDllNotifications(LdrEntry, LDR_DLL_NOTIFICATION_REASON_LOADED);
1231+
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN8)
1232+
LdrEntry->Flags |= LDRP_LOAD_NOTIFICATIONS_SENT; /* LdrEntry->LoadNotificationsSent = TRUE; */
1233+
#endif
1234+
#endif
12301235

12311236
/* Check for invalid CPU Image */
12321237
if (Status == STATUS_IMAGE_MACHINE_TYPE_MISMATCH)

dll/ntdll/nt_0600/CMakeLists.txt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ include_directories(
1111
${REACTOS_SOURCE_DIR}/sdk/include/reactos/subsys)
1212

1313
list(APPEND SOURCE
14-
DllMain.c
15-
${CMAKE_CURRENT_BINARY_DIR}/ntdll_vista.def)
14+
ldr/ldrinit.c
15+
ldr/ldrnotify.c)
1616

17-
add_library(ntdll_vista MODULE ${SOURCE} ntdll_vista.rc)
17+
add_library(ntdll_vista_static ${SOURCE})
18+
target_link_libraries(ntdll_vista_static)
19+
add_dependencies(ntdll_vista_static psdk)
20+
add_library(ntdll_vista MODULE DllMain.c ${CMAKE_CURRENT_BINARY_DIR}/ntdll_vista.def ntdll_vista.rc)
1821
set_module_type(ntdll_vista win32dll ENTRYPOINT DllMain 12)
19-
target_link_libraries(ntdll_vista smlib rtl_vista)
22+
target_link_libraries(ntdll_vista ntdll_vista_static smlib rtl_vista ${PSEH_LIB})
2023
if(ARCH STREQUAL "arm")
2124
target_link_libraries(ntdll_vista chkstk)
2225
endif()

dll/ntdll/nt_0600/DllMain.c

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,4 @@
1-
#include <stdarg.h>
2-
3-
#define WIN32_NO_STATUS
4-
5-
#include <windef.h>
6-
#include <winbase.h>
7-
#include <winreg.h>
8-
#include <winuser.h>
9-
#include <winwlx.h>
10-
#include <ndk/rtltypes.h>
11-
#include <ndk/umfuncs.h>
12-
13-
#define NDEBUG
14-
#include <debug.h>
15-
16-
VOID
17-
NTAPI
18-
RtlpInitializeKeyedEvent(VOID);
19-
20-
VOID
21-
NTAPI
22-
RtlpCloseKeyedEvent(VOID);
1+
#include "ntdll_vista.h"
232

243
BOOL
254
WINAPI

dll/ntdll/nt_0600/ldr/ldrinit.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include "ntdll_vista.h"
2+
3+
PVOID LdrpHeap;

dll/ntdll/nt_0600/ldr/ldrnotify.c

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
/*
2+
* PROJECT: ReactOS NT Layer/System API
3+
* LICENSE: MIT (https://spdx.org/licenses/MIT)
4+
* PURPOSE: DLL Load Notification Implementation
5+
* COPYRIGHT: Copyright 2024 Ratin Gao <[email protected]>
6+
*/
7+
8+
#include "ntdll_vista.h"
9+
10+
/* GLOBALS *******************************************************************/
11+
12+
typedef struct _LDR_DLL_NOTIFICATION_ENTRY
13+
{
14+
LIST_ENTRY List;
15+
PLDR_DLL_NOTIFICATION_FUNCTION Callback;
16+
PVOID Context;
17+
} LDR_DLL_NOTIFICATION_ENTRY, *PLDR_DLL_NOTIFICATION_ENTRY;
18+
19+
static RTL_STATIC_LIST_HEAD(LdrpDllNotificationList);
20+
21+
/* Initialize critical section statically */
22+
static RTL_CRITICAL_SECTION LdrpDllNotificationLock;
23+
static RTL_CRITICAL_SECTION_DEBUG LdrpDllNotificationLockDebug = {
24+
.CriticalSection = &LdrpDllNotificationLock
25+
};
26+
static RTL_CRITICAL_SECTION LdrpDllNotificationLock = {
27+
&LdrpDllNotificationLockDebug,
28+
-1,
29+
0,
30+
0,
31+
0,
32+
0
33+
};
34+
35+
/* FUNCTIONS *****************************************************************/
36+
37+
NTSTATUS
38+
NTAPI
39+
LdrRegisterDllNotification(
40+
_In_ ULONG Flags,
41+
_In_ PLDR_DLL_NOTIFICATION_FUNCTION NotificationFunction,
42+
_In_opt_ PVOID Context,
43+
_Out_ PVOID *Cookie)
44+
{
45+
PLDR_DLL_NOTIFICATION_ENTRY NewEntry;
46+
47+
/* Check input parameters */
48+
if (Flags != 0 || NotificationFunction == NULL || Cookie == NULL)
49+
{
50+
return STATUS_INVALID_PARAMETER;
51+
}
52+
53+
/* Allocate new entry and assign input values */
54+
NewEntry = RtlAllocateHeap(LdrpHeap, 0, sizeof(*NewEntry));
55+
if (NewEntry == NULL)
56+
{
57+
return STATUS_NO_MEMORY;
58+
}
59+
NewEntry->Callback = NotificationFunction;
60+
NewEntry->Context = Context;
61+
62+
/* Add node to the end of global list */
63+
RtlEnterCriticalSection(&LdrpDllNotificationLock);
64+
InsertTailList(&LdrpDllNotificationList, &NewEntry->List);
65+
RtlLeaveCriticalSection(&LdrpDllNotificationLock);
66+
67+
/* Cookie is address of the new entry */
68+
*Cookie = NewEntry;
69+
return STATUS_SUCCESS;
70+
}
71+
72+
NTSTATUS
73+
NTAPI
74+
LdrUnregisterDllNotification(
75+
_In_ PVOID Cookie)
76+
{
77+
NTSTATUS Status = STATUS_DLL_NOT_FOUND;
78+
PLIST_ENTRY Entry;
79+
80+
/* Find entry to remove */
81+
RtlEnterCriticalSection(&LdrpDllNotificationLock);
82+
for (Entry = LdrpDllNotificationList.Flink;
83+
Entry != &LdrpDllNotificationList;
84+
Entry = Entry->Flink)
85+
{
86+
if (Entry == Cookie)
87+
{
88+
RemoveEntryList(Entry);
89+
Status = STATUS_SUCCESS;
90+
break;
91+
}
92+
}
93+
RtlLeaveCriticalSection(&LdrpDllNotificationLock);
94+
95+
if (NT_SUCCESS(Status))
96+
{
97+
RtlFreeHeap(LdrpHeap, 0, Entry);
98+
}
99+
return Status;
100+
}
101+
102+
VOID
103+
NTAPI
104+
LdrpSendDllNotifications(
105+
_In_ PLDR_DATA_TABLE_ENTRY DllEntry,
106+
_In_ ULONG NotificationReason)
107+
{
108+
PLIST_ENTRY Entry;
109+
PLDR_DLL_NOTIFICATION_ENTRY NotificationEntry;
110+
LDR_DLL_NOTIFICATION_DATA NotificationData;
111+
112+
/*
113+
* LDR_DLL_LOADED_NOTIFICATION_DATA and LDR_DLL_UNLOADED_NOTIFICATION_DATA
114+
* currently are the same. Use C_ASSERT to ensure it, then fill either of them.
115+
*/
116+
#define LdrpAssertDllNotificationDataMember(x)\
117+
C_ASSERT(FIELD_OFFSET(LDR_DLL_NOTIFICATION_DATA, Loaded.x) ==\
118+
FIELD_OFFSET(LDR_DLL_NOTIFICATION_DATA, Unloaded.x))
119+
120+
C_ASSERT(sizeof(NotificationData.Loaded) == sizeof(NotificationData.Unloaded));
121+
LdrpAssertDllNotificationDataMember(Flags);
122+
LdrpAssertDllNotificationDataMember(FullDllName);
123+
LdrpAssertDllNotificationDataMember(BaseDllName);
124+
LdrpAssertDllNotificationDataMember(DllBase);
125+
LdrpAssertDllNotificationDataMember(SizeOfImage);
126+
127+
#undef LdrpAssertDllNotificationDataMember
128+
129+
NotificationData.Loaded.Flags = 0; /* Reserved and always 0, not DllEntry->Flags */
130+
NotificationData.Loaded.FullDllName = &DllEntry->FullDllName;
131+
NotificationData.Loaded.BaseDllName = &DllEntry->BaseDllName;
132+
NotificationData.Loaded.DllBase = DllEntry->DllBase;
133+
NotificationData.Loaded.SizeOfImage = DllEntry->SizeOfImage;
134+
135+
/* Send notification to all registered callbacks */
136+
RtlEnterCriticalSection(&LdrpDllNotificationLock);
137+
_SEH2_TRY
138+
{
139+
for (Entry = LdrpDllNotificationList.Flink;
140+
Entry != &LdrpDllNotificationList;
141+
Entry = Entry->Flink)
142+
{
143+
NotificationEntry = CONTAINING_RECORD(Entry, LDR_DLL_NOTIFICATION_ENTRY, List);
144+
NotificationEntry->Callback(NotificationReason,
145+
&NotificationData,
146+
NotificationEntry->Context);
147+
}
148+
}
149+
_SEH2_FINALLY
150+
{
151+
RtlLeaveCriticalSection(&LdrpDllNotificationLock);
152+
}
153+
_SEH2_END;
154+
}

0 commit comments

Comments
 (0)