[v2 PATCH] ntdll: Implement LdrRegisterDllNotification and LdrUnregisterDllNotification.

Alistair Leslie-Hughes leslie_alistair at hotmail.com
Thu Jun 21 20:54:56 CDT 2018


From: Michael Müller <michael at fds-team.de>

Signed-off-by: Alistair Leslie-Hughes <leslie_alistair at hotmail.com>
---
 dlls/ntdll/loader.c    |  77 ++++++++++++++-
 dlls/ntdll/tests/rtl.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 334 insertions(+), 4 deletions(-)

diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c
index 3e00ec6..df206e8 100644
--- a/dlls/ntdll/loader.c
+++ b/dlls/ntdll/loader.c
@@ -40,6 +40,7 @@
 #include "wine/library.h"
 #include "wine/unicode.h"
 #include "wine/debug.h"
+#include "wine/list.h"
 #include "wine/server.h"
 #include "ntdll_misc.h"
 #include "ddk/wdm.h"
@@ -71,6 +72,15 @@ static BOOL imports_fixup_done = FALSE;  /* set once the imports have been fixed
 static BOOL process_detaching = FALSE;  /* set on process detach to avoid deadlocks with thread detach */
 static int free_lib_count;   /* recursion depth of LdrUnloadDll calls */
 
+struct ldr_notification
+{
+    struct list                    entry;
+    PLDR_DLL_NOTIFICATION_FUNCTION callback;
+    void                           *context;
+};
+
+static struct list ldr_notifications = LIST_INIT( ldr_notifications );
+
 static const char * const reason_names[] =
 {
     "PROCESS_DETACH",
@@ -342,6 +352,29 @@ static ULONG_PTR allocate_stub( const char *dll, const char *name )
 static inline ULONG_PTR allocate_stub( const char *dll, const char *name ) { return 0xdeadbeef; }
 #endif  /* __i386__ */
 
+/* call ldr notifications */
+static void call_ldr_notifications( ULONG reason, LDR_MODULE *module )
+{
+    struct ldr_notification *notify, *notify_next;
+    LDR_DLL_NOTIFICATION_DATA data;
+
+    data.Loaded.Flags       = 0;
+    data.Loaded.FullDllName = &module->FullDllName;
+    data.Loaded.BaseDllName = &module->BaseDllName;
+    data.Loaded.DllBase     = module->BaseAddress;
+    data.Loaded.SizeOfImage = module->SizeOfImage;
+
+    LIST_FOR_EACH_ENTRY_SAFE( notify, notify_next, &ldr_notifications, struct ldr_notification, entry )
+    {
+        TRACE_(relay)("\1Call LDR notification callback (proc=%p,reason=%u,data=%p,context=%p)\n",
+                notify->callback, reason, &data, notify->context );
+
+        notify->callback(reason, &data, notify->context);
+
+        TRACE_(relay)("\1Ret  LDR notification callback (proc=%p,reason=%u,data=%p,context=%p)\n",
+                notify->callback, reason, &data, notify->context );
+    }
+}
 
 /*************************************************************************
  *		get_modref
@@ -1275,12 +1308,18 @@ static NTSTATUS process_attach( WINE_MODREF *wm, LPVOID lpReserved )
     {
         WINE_MODREF *prev = current_modref;
         current_modref = wm;
+
+        call_ldr_notifications( LDR_DLL_NOTIFICATION_REASON_LOADED, &wm->ldr );
         status = MODULE_InitDLL( wm, DLL_PROCESS_ATTACH, lpReserved );
         if (status == STATUS_SUCCESS)
+        {
             wm->ldr.Flags |= LDR_PROCESS_ATTACHED;
+        }
         else
         {
             MODULE_InitDLL( wm, DLL_PROCESS_DETACH, lpReserved );
+            call_ldr_notifications( LDR_DLL_NOTIFICATION_REASON_UNLOADED, &wm->ldr );
+
             /* point to the name so LdrInitializeThunk can print it */
             last_failed_modref = wm;
             WARN("Initialization of %s failed\n", debugstr_w(wm->ldr.BaseDllName.Buffer));
@@ -1353,6 +1392,7 @@ static void process_detach(void)
             mod->Flags &= ~LDR_PROCESS_ATTACHED;
             MODULE_InitDLL( CONTAINING_RECORD(mod, WINE_MODREF, ldr), 
                             DLL_PROCESS_DETACH, ULongToPtr(process_detaching) );
+            call_ldr_notifications( LDR_DLL_NOTIFICATION_REASON_UNLOADED, mod );
 
             /* Restart at head of WINE_MODREF list, as entries might have
                been added and/or removed while performing the call ... */
@@ -1467,8 +1507,27 @@ NTSTATUS WINAPI LdrEnumerateLoadedModules( void *unknown, LDRENUMPROC callback,
 NTSTATUS WINAPI LdrRegisterDllNotification(ULONG flags, PLDR_DLL_NOTIFICATION_FUNCTION callback,
                                            void *context, void **cookie)
 {
-    FIXME( "(%04x, %p, %p, %p) stub\n", flags, callback, context, cookie );
-    return STATUS_NOT_IMPLEMENTED;
+    struct ldr_notification *notify;
+
+    TRACE( "(%x, %p, %p, %p)\n", flags, callback, context, cookie );
+
+    if (!callback || !cookie)
+        return STATUS_INVALID_PARAMETER;
+
+    if (flags)
+        FIXME( "ignoring flags %x\n", flags );
+
+    notify = RtlAllocateHeap( GetProcessHeap(), 0, sizeof(*notify) );
+    if (!notify) return STATUS_NO_MEMORY;
+    notify->callback = callback;
+    notify->context = context;
+
+    RtlEnterCriticalSection( &loader_section );
+    list_add_tail( &ldr_notifications, &notify->entry );
+    RtlLeaveCriticalSection( &loader_section );
+
+    *cookie = notify;
+    return STATUS_SUCCESS;
 }
 
 /******************************************************************
@@ -1476,8 +1535,18 @@ NTSTATUS WINAPI LdrRegisterDllNotification(ULONG flags, PLDR_DLL_NOTIFICATION_FU
  */
 NTSTATUS WINAPI LdrUnregisterDllNotification( void *cookie )
 {
-    FIXME( "(%p) stub\n", cookie );
-    return STATUS_NOT_IMPLEMENTED;
+    struct ldr_notification *notify = cookie;
+
+    TRACE( "(%p)\n", cookie );
+
+    if (!notify) return STATUS_INVALID_PARAMETER;
+
+    RtlEnterCriticalSection( &loader_section );
+    list_remove( &notify->entry );
+    RtlLeaveCriticalSection( &loader_section );
+
+    RtlFreeHeap( GetProcessHeap(), 0, notify );
+    return STATUS_SUCCESS;
 }
 
 /******************************************************************
diff --git a/dlls/ntdll/tests/rtl.c b/dlls/ntdll/tests/rtl.c
index e202408..881bc1a 100644
--- a/dlls/ntdll/tests/rtl.c
+++ b/dlls/ntdll/tests/rtl.c
@@ -101,6 +101,8 @@ static NTSTATUS  (WINAPI *pRtlInitializeCriticalSectionEx)(CRITICAL_SECTION *, U
 static NTSTATUS  (WINAPI *pLdrEnumerateLoadedModules)(void *, void *, void *);
 static NTSTATUS  (WINAPI *pRtlMakeSelfRelativeSD)(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,LPDWORD);
 static NTSTATUS  (WINAPI *pRtlAbsoluteToSelfRelativeSD)(PSECURITY_DESCRIPTOR,PSECURITY_DESCRIPTOR,PULONG);
+static NTSTATUS  (WINAPI *pLdrRegisterDllNotification)(ULONG, PLDR_DLL_NOTIFICATION_FUNCTION, void *, void **);
+static NTSTATUS  (WINAPI *pLdrUnregisterDllNotification)(void *);
 
 static HMODULE hkernel32 = 0;
 static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
@@ -108,10 +110,15 @@ static BOOL      (WINAPI *pIsWow64Process)(HANDLE, PBOOL);
 
 #define LEN 16
 static const char* src_src = "This is a test!"; /* 16 bytes long, incl NUL */
+static WCHAR ws2_32dllW[] = {'w','s','2','_','3','2','.','d','l','l',0};
+static WCHAR nsidllW[]    = {'n','s','i','.','d','l','l',0};
+static WCHAR wintrustdllW[] = {'w','i','n','t','r','u','s','t','.','d','l','l',0};
+static WCHAR crypt32dllW[] = {'c','r','y','p','t','3','2','.','d','l','l',0};
 static ULONG src_aligned_block[4];
 static ULONG dest_aligned_block[32];
 static const char *src = (const char*)src_aligned_block;
 static char* dest = (char*)dest_aligned_block;
+const WCHAR *expected_dll = nsidllW;
 
 static void InitFunctionPtrs(void)
 {
@@ -157,6 +164,8 @@ static void InitFunctionPtrs(void)
         pLdrEnumerateLoadedModules = (void *)GetProcAddress(hntdll, "LdrEnumerateLoadedModules");
         pRtlMakeSelfRelativeSD = (void *)GetProcAddress(hntdll, "RtlMakeSelfRelativeSD");
         pRtlAbsoluteToSelfRelativeSD = (void *)GetProcAddress(hntdll, "RtlAbsoluteToSelfRelativeSD");
+        pLdrRegisterDllNotification = (void *)GetProcAddress(hntdll, "LdrRegisterDllNotification");
+        pLdrUnregisterDllNotification = (void *)GetProcAddress(hntdll, "LdrUnregisterDllNotification");
     }
     hkernel32 = LoadLibraryA("kernel32.dll");
     ok(hkernel32 != 0, "LoadLibrary failed\n");
@@ -2257,6 +2266,257 @@ static void test_RtlMakeSelfRelativeSD(void)
     ok( status == STATUS_BAD_DESCRIPTOR_FORMAT, "got %08x\n", status );
 }
 
+static DWORD (CALLBACK *orig_entry)(HMODULE,DWORD,LPVOID);
+static DWORD *dll_main_data;
+
+static inline void *get_rva( HMODULE module, DWORD va )
+{
+    return (void *)((char *)module + va);
+}
+
+static void CALLBACK ldr_notify_callback1(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    const IMAGE_IMPORT_DESCRIPTOR *imports;
+    const IMAGE_THUNK_DATA *import_list;
+    IMAGE_THUNK_DATA *thunk_list;
+    DWORD *calls = context;
+    LIST_ENTRY *mark;
+    LDR_MODULE *mod;
+    ULONG size;
+    int i, j;
+
+    *calls <<= 4;
+    *calls |= reason;
+
+    if (!lstrcmpiW(data->Loaded.BaseDllName->Buffer, expected_dll))
+        return;
+
+    ok(data->Loaded.Flags == 0, "Expected flags 0, got %x\n", data->Loaded.Flags);
+    ok(!lstrcmpiW(data->Loaded.BaseDllName->Buffer, expected_dll), "Expected %s, got %s\n",
+       wine_dbgstr_w(expected_dll), wine_dbgstr_w(data->Loaded.BaseDllName->Buffer));
+    ok(!!data->Loaded.DllBase, "Expected non zero base address\n");
+    ok(data->Loaded.SizeOfImage, "Expected non zero image size\n");
+
+    /* expect module to be last module listed in LdrData load order list */
+    mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
+    mod = CONTAINING_RECORD(mark->Blink, LDR_MODULE, InMemoryOrderModuleList);
+    ok(mod->BaseAddress == data->Loaded.DllBase, "Expected base address %p, got %p\n",
+       data->Loaded.DllBase, mod->BaseAddress);
+    ok(!lstrcmpiW(mod->BaseDllName.Buffer, expected_dll), "Expected %s, got %s\n",
+       wine_dbgstr_w(expected_dll), wine_dbgstr_w(mod->BaseDllName.Buffer));
+
+    /* show that imports have already been resolved */
+    imports = RtlImageDirectoryEntryToData(data->Loaded.DllBase, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size);
+    ok(!!imports, "Expected dll to have imports\n");
+
+    for (i = 0; imports[i].Name; i++)
+    {
+        thunk_list = get_rva(data->Loaded.DllBase, (DWORD)imports[i].FirstThunk);
+        if (imports[i].OriginalFirstThunk)
+            import_list = get_rva(data->Loaded.DllBase, (DWORD)imports[i].OriginalFirstThunk);
+        else
+            import_list = thunk_list;
+
+        for (j = 0; import_list[j].u1.Ordinal; j++)
+        {
+            ok(thunk_list[j].u1.AddressOfData > data->Loaded.SizeOfImage,
+               "Import has not been resolved: %p\n", (void*)thunk_list[j].u1.Function);
+        }
+    }
+}
+
+static void CALLBACK ldr_notify_callback2(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    DWORD *calls = context;
+    *calls <<= 4;
+    *calls |= reason + 2;
+}
+
+static BOOL WINAPI fake_dll_main(HINSTANCE instance, DWORD reason, void* reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH)
+    {
+        *dll_main_data <<= 4;
+        *dll_main_data |= 3;
+    }
+    else if (reason == DLL_PROCESS_DETACH)
+    {
+        *dll_main_data <<= 4;
+        *dll_main_data |= 4;
+    }
+    return orig_entry(instance, reason, reserved);
+}
+
+static void CALLBACK ldr_notify_callback_dll_main(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    DWORD *calls = context;
+    LIST_ENTRY *mark;
+    LDR_MODULE *mod;
+
+    *calls <<= 4;
+    *calls |= reason;
+
+    if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED)
+        return;
+
+    mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
+    mod = CONTAINING_RECORD(mark->Blink, LDR_MODULE, InMemoryOrderModuleList);
+    ok(mod->BaseAddress == data->Loaded.DllBase, "Expected base address %p, got %p\n",
+       data->Loaded.DllBase, mod->BaseAddress);
+    if (mod->BaseAddress != data->Loaded.DllBase)
+       return;
+
+    orig_entry = mod->EntryPoint;
+    mod->EntryPoint = fake_dll_main;
+    dll_main_data = calls;
+}
+
+static BOOL WINAPI fake_dll_main_fail(HINSTANCE instance, DWORD reason, void* reserved)
+{
+    if (reason == DLL_PROCESS_ATTACH)
+    {
+        *dll_main_data <<= 4;
+        *dll_main_data |= 3;
+    }
+    else if (reason == DLL_PROCESS_DETACH)
+    {
+        *dll_main_data <<= 4;
+        *dll_main_data |= 4;
+    }
+    return FALSE;
+}
+
+static void CALLBACK ldr_notify_callback_fail(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    DWORD *calls = context;
+    LIST_ENTRY *mark;
+    LDR_MODULE *mod;
+
+    *calls <<= 4;
+    *calls |= reason;
+
+    if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED)
+        return;
+
+    mark = &NtCurrentTeb()->Peb->LdrData->InMemoryOrderModuleList;
+    mod = CONTAINING_RECORD(mark->Blink, LDR_MODULE, InMemoryOrderModuleList);
+    ok(mod->BaseAddress == data->Loaded.DllBase, "Expected base address %p, got %p\n",
+       data->Loaded.DllBase, mod->BaseAddress);
+    if (mod->BaseAddress != data->Loaded.DllBase)
+       return;
+
+    orig_entry = mod->EntryPoint;
+    mod->EntryPoint = fake_dll_main_fail;
+    dll_main_data = calls;
+}
+
+static void CALLBACK ldr_notify_callback_imports(ULONG reason, LDR_DLL_NOTIFICATION_DATA *data, void *context)
+{
+    DWORD *calls = context;
+
+    if (reason != LDR_DLL_NOTIFICATION_REASON_LOADED)
+        return;
+
+    if (!lstrcmpiW(data->Loaded.BaseDllName->Buffer, crypt32dllW))
+    {
+        *calls <<= 4;
+        *calls |= 1;
+    }
+
+    if (!lstrcmpiW(data->Loaded.BaseDllName->Buffer, wintrustdllW))
+    {
+        *calls <<= 4;
+        *calls |= 2;
+    }
+}
+
+static void test_LdrRegisterDllNotification(void)
+{
+    void *cookie, *cookie2;
+    NTSTATUS status;
+    HMODULE mod;
+    DWORD calls;
+
+    if (!pLdrRegisterDllNotification || !pLdrUnregisterDllNotification)
+    {
+        win_skip("Ldr(Un)RegisterDllNotification not available\n");
+        return;
+    }
+
+    mod = LoadLibraryW(expected_dll);
+    if(mod)
+        FreeLibrary(mod);
+    else
+        expected_dll = ws2_32dllW; /* XP Default */
+
+    /* generic test */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback1, &calls, &cookie);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(expected_dll);
+    ok(!!mod, "Failed to load library: %d\n", GetLastError());
+    ok(calls == LDR_DLL_NOTIFICATION_REASON_LOADED, "Expected LDR_DLL_NOTIFICATION_REASON_LOADED, got %x\n", calls);
+
+    calls = 0;
+    FreeLibrary(mod);
+    ok(calls == LDR_DLL_NOTIFICATION_REASON_UNLOADED, "Expected LDR_DLL_NOTIFICATION_REASON_UNLOADED, got %x\n", calls);
+
+    /* test order of callbacks */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback2, &calls, &cookie2);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(expected_dll);
+    ok(!!mod, "Failed to load library: %d\n", GetLastError());
+    ok(calls == 0x13, "Expected order 0x13, got %x\n", calls);
+
+    calls = 0;
+    FreeLibrary(mod);
+    ok(calls == 0x24, "Expected order 0x24, got %x\n", calls);
+
+    pLdrUnregisterDllNotification(cookie2);
+    pLdrUnregisterDllNotification(cookie);
+
+    /* test dll main order */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback_dll_main, &calls, &cookie);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(expected_dll);
+    ok(!!mod, "Failed to load library: %d\n", GetLastError());
+    ok(calls == 0x13, "Expected order 0x13, got %x\n", calls);
+
+    calls = 0;
+    FreeLibrary(mod);
+    ok(calls == 0x42, "Expected order 0x42, got %x\n", calls);
+
+    pLdrUnregisterDllNotification(cookie);
+
+    /* test dll main order */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback_fail, &calls, &cookie);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(expected_dll);
+    ok(!mod, "Expected library to fail loading\n");
+    ok(calls == 0x1342, "Expected order 0x1342, got %x\n", calls);
+
+    pLdrUnregisterDllNotification(cookie);
+
+    /* test dll with dependencies */
+    status = pLdrRegisterDllNotification(0, ldr_notify_callback_imports, &calls, &cookie);
+    ok(!status, "Expected STATUS_SUCCESS, got %08x\n", status);
+
+    calls = 0;
+    mod = LoadLibraryW(wintrustdllW);
+    ok(!!mod, "Failed to load library: %d\n", GetLastError());
+    ok(calls == 0x12 || calls == 0x21, "got %x\n", calls);
+
+    FreeLibrary(mod);
+    pLdrUnregisterDllNotification(cookie);
+}
+
 START_TEST(rtl)
 {
     InitFunctionPtrs();
@@ -2291,4 +2551,5 @@ START_TEST(rtl)
     test_RtlLeaveCriticalSection();
     test_LdrEnumerateLoadedModules();
     test_RtlMakeSelfRelativeSD();
+    test_LdrRegisterDllNotification();
 }
-- 
1.9.1



More information about the wine-devel mailing list