From: Alistair Leslie-Hughes Subject: [PATCH] ntdll: Implement LdrRegisterDllNotification and LdrUnregisterDllNotification. Message-Id: Date: Thu, 21 Jun 2018 05:30:06 +0000 From: Michael Müller Signed-off-by: Alistair Leslie-Hughes --- dlls/ntdll/loader.c | 75 +++++++++++++- dlls/ntdll/tests/rtl.c | 261 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 332 insertions(+), 4 deletions(-) diff --git a/dlls/ntdll/loader.c b/dlls/ntdll/loader.c index 3e00ec6..cae9b25 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,13 @@ 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 const char * const reason_names[] = { "PROCESS_DETACH", @@ -342,6 +350,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 +1306,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 +1390,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 +1505,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, ¬ify->entry ); + RtlLeaveCriticalSection( &loader_section ); + + *cookie = notify; + return STATUS_SUCCESS; } /****************************************************************** @@ -1476,8 +1533,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( ¬ify->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 2e3a7de..1a9e313 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"); @@ -2259,6 +2268,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(); @@ -2293,4 +2553,5 @@ START_TEST(rtl) test_RtlLeaveCriticalSection(); test_LdrEnumerateLoadedModules(); test_RtlMakeSelfRelativeSD(); + test_LdrRegisterDllNotification(); } -- 1.9.1