天道酬勤,学无止境

从 DllMain 加载/调用 ntdll(Loading/calling ntdll from DllMain)

问题

不应使用DllMainkernel32.dll以外的函数:

来自 MS 文档:

因为在调用入口点函数时 Kernel32.dll 被保证加载到进程地址空间中,所以调用 Kernel32.dll 中的函数不会导致在执行其初始化代码之前使用该 DLL。 因此,入口函数可以调用 Kernel32.dll 中不加载其他 DLL 的函数。 例如,DllMain 可以创建关键部分和互斥锁等同步对象,并使用 TLS。 不幸的是,Kernel32.dll 中没有完整的安全函数列表。
...
调用需要 Kernel32.dll 以外的 DLL 的函数可能会导致难以诊断的问题。 例如,调用 User、Shell 和 COM 函数可能会导致访问冲突错误,因为某些函数会加载其他系统组件。 相反,在终止期间调用这些函数可能会导致访问冲突错误,因为相应的组件可能已经被卸载或未初始化。

我的问题:
但文档没有提到ntdll.dll 。 - 我可以为“ntdll”调用LoadLibrary并从DllMain使用ntdll 中的函数:
1) 在DLL_PROCESS_ATTACH期间(加载和使用 ntdll 的函数)?
2) 在DLL_PROCESS_DETACH期间(使用先前加载的 ntdll 的函数)?


另外,请问拥有 1500 多个声誉的人会喜欢创建一个名为“dllmain”的新标签吗?

回答1

问题“在 DllMain 中是否安全”的答案始终默认为“否”。 在这种情况下,调用LoadLibrary永远不会好。

一般来说,即使在安全的地方,也不建议调用ntdll.dll任何内容。

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • Loading/calling ntdll from DllMain
    One should not use functions other than those in kernel32.dll from DllMain: From MS documentation: Because Kernel32.dll is guaranteed to be loaded in the process address space when the entry-point function is called, calling functions in Kernel32.dll does not result in the DLL being used before its initialization code has been executed. Therefore, the entry-point function can call functions in Kernel32.dll that do not load other DLLs. For example, DllMain can create synchronization objects such as critical sections and mutexes, and use TLS. Unfortunately, there is not a comprehensive list of
  • 我们可以从 ExitInstance 调用 FreeLibrary(Can we call FreeLibrary from ExitInstance)
    问题 从MSDN文档中我们可以看出,在DllMain入口点函数中不应该调用LoadLibrary/FreeLibrary。 入口函数应该只执行简单的初始化或终止任务。 它不能调用 LoadLibrary 或 LoadLibraryEx 函数(或调用这些函数的函数),因为这可能会在 DLL 加载顺序中创建依赖循环。 这可能导致在系统执行其初始化代码之前使用 DLL。 类似地,入口函数在进程终止期间不得调用 FreeLibrary 函数(或调用 FreeLibrary 的函数),因为这可能导致在系统执行其终止代码后使用 DLL。 我的问题是:我们可以从 ExitInstance() 调用 FreeLibrary 吗? 例如: Test.exe - 主要可执行文件 HINSTANCE hDllMFC = LoadLibrary(L"TestApp.dll"); if (hDllMFC != NULL) { FreeLibrary(hDllMFC); } while unload the hDllMFC, the call stack looks like: TestApp.dll!CTestAppApp::ExitInstance() Line 42 C++ TestApp.dll!InternalDllMain() Line 155 C++ TestApp.dll!DllMain()
  • windbg 故障转储分析,cpu 使用率高 -(windbg crash dump analysis, high cpu usage -)
    问题 我的应用程序(web api)受到高 CPU 的影响,在分析转储时,我看到我的大部分线程都有这个 !dumpstack -: Child-SP RetAddr Caller, Callee 00000030497bec00 00007ffbb19e1118 KERNELBASE!WaitForSingleObjectEx+0x94, calling ntdll!NtWaitForSingleObject 00000030497beca0 00007ffba8375dda clr!CLRSemaphore::Wait+0xee, calling kernel32!WaitForSingleObjectEx 00000030497becd0 00007ffba837345d clr!GCCoop::GCCoop+0xe, calling clr!GetThread 00000030497bed60 00007ffba8375842 clr!ThreadpoolMgr::WorkerThreadStart+0x482, calling clr!CLRSemaphore::Wait 00000030497bee00 00007ffba8393e1e clr!Thread::intermediateThreadProc+0x7d 00000030497bee30
  • C++11 std::mutex in Visual Studio 2012 deadlock when locked from DllMain()
    I am seeing a deadlock with std::mutex when the mutex is locked from DllMain() Below is a minimal DLL test case that exhibits the problem for me. My actual code does the mutex locking because it uses member functions that are also usable outside initialization during normal function. I think that the problem is a deadlock between the scheduler as seen in the call stack of main() thread and the other thread (probably) spawned by the scheduler. The deadlock seems to happen before main() is actually executed. I would appreciate any advice as to how to fix/resolve the deadlock. Simple DLL: static
  • std::thread 导致 DLLMain 中的死锁(std::thread cause deadlock in DLLMain)
    问题 所以,这就是我要说的:std 很复杂。 在 VS2013 中,这个简单的程序会导致死锁。 #include <thread> #include <windows.h> void foo() { } void initialize() { std::thread t(foo); } BOOL APIENTRY DllMain(HMODULE, DWORD reason, LPVOID) { switch (reason) { case DLL_PROCESS_ATTACH: initialize(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } return TRUE; } 在 DLLMain 中创建线程是完全错误的吗? 这不是真的。 来自 Microsoft 的文档“Best Practices for Creating DLLs”:“如果不与其他线程同步,则创建线程可以工作”。 所以 CreateThread 有效,_beginthreadex 有效,boost::thread 有效,但 std::thread 无效。 这是调用堆栈: ntdll.dll!_NtWaitForSingleObject@12()
  • windbg crash dump analysis, high cpu usage -
    My application(web api) is suffering with high cpu, while analyzing dump, I see my most of the threads have this !dumpstack -: Child-SP RetAddr Caller, Callee 00000030497bec00 00007ffbb19e1118 KERNELBASE!WaitForSingleObjectEx+0x94, calling ntdll!NtWaitForSingleObject 00000030497beca0 00007ffba8375dda clr!CLRSemaphore::Wait+0xee, calling kernel32!WaitForSingleObjectEx 00000030497becd0 00007ffba837345d clr!GCCoop::GCCoop+0xe, calling clr!GetThread 00000030497bed60 00007ffba8375842 clr!ThreadpoolMgr::WorkerThreadStart+0x482, calling clr!CLRSemaphore::Wait 00000030497bee00 00007ffba8393e1e clr
  • 检测何时卸载模块 (DLL)(Detect when a Module (DLL) is unloaded)
    问题 有没有办法以编程方式检测模块(特别是 DLL)何时从进程中卸载? 我没有 DLL 源,所以我无法更改它的 DLL 入口点。 我也不能轮询当前是否加载了 DLL,因为 DLL 可能会在轮询之间被卸载然后重新加载。 结果: 我最终使用了绕过 dll 入口点并捕获 DLL_PROCESS_DETACH 的 jimharks 解决方案。 我发现绕道 FreeLibrary() 也可以工作,但必须添加代码以检测模块何时实际卸载或引用计数是否正在减少。 Necrolis 关于查找引用计数的链接对于这样做的方法很方便。 我应该注意的是,如果 MSDetours 中存在绕道,则我在使用 MSDetours 时实际上不会从内存中卸载模块。 回答1 也许比 Necrolis 更糟糕的方法是使用 Microsoft Research 的 Detours 包来挂钩 dll 的入口点以监视 DLL_PROCESS_DETACH 通知。 您可以使用以下函数找到给定 HMODULE(由 LoadLibrary 返回)的入口点: #include <windows.h> #include <DelayImp.h> PVOID GetAddressOfEntryPoint(HMODULE hmod) { PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)hmod
  • 是否有来自 kernel32.dll 或 ntdll.dll 等低级库的 wsprintf() 类型的函数?(Is there a wsprintf()-type function from a low-level library such as kernel32.dll or ntdll.dll?)
    问题 我正在编写一个低级记录器函数,它将文本字符串附加到文本(日志)文件的末尾。 要求是此函数不应从进程可能尚不可用的 DLL 中调用任何 WinAPI,例如当它从 DllMain 处理程序调用时。 换句话说,它不能使用除了保证加载到任何用户模式进程的库之外的任何库,即kernel32.dll或ntdll.dll 。 我只使用CreateFile 、 WriteFile 、 CloseHandle 、 HeapAlloc 、 HeapFree等都来自kernel32.dll就可以很好地解决问题。 问题是格式化输出字符串。 例如,我需要添加一些额外的(自动生成的)的详细信息,诸如当前时间,过程ID,会话ID等我通常会使用wsprintf型函数,该函数,或StringCchPrintf准确的说,因为这样: StringCchPrintf(buffer, buffer_size, L"%04u-%02u-%02u %02u:%02u:%02u pid=0x%x, sessID=%d, %s\r\n", /* parameters */ ); 但是这些 API 违反了我上面提到的规则。 有谁知道是否有可用的低级printf类型格式化 API? 回答1 所有版本的ntdll.dll 都支持最小 next(from xp) 字符串格式化函数: _snprintf _snwprintf
  • std::thread cause deadlock in DLLMain
    So, this is what I'm talking about: std is complex. In VS2013 this simple program will cause a deadlock. #include <thread> #include <windows.h> void foo() { } void initialize() { std::thread t(foo); } BOOL APIENTRY DllMain(HMODULE, DWORD reason, LPVOID) { switch (reason) { case DLL_PROCESS_ATTACH: initialize(); break; case DLL_THREAD_ATTACH: break; case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: break; } return TRUE; } Create a thread in DLLMain is totally wrong ? It's not true. From the document "Best Practices for Creating DLLs" of Microsoft: "Creating a thread can work if you do not
  • Can we call FreeLibrary from ExitInstance
    From the MSDN document, we can see that, we should not call LoadLibrary/FreeLibrary in the DllMain entry point function. The entry-point function should perform only simple initialization or termination tasks. It must not call the LoadLibrary or LoadLibraryEx function (or a function that calls these functions), because this may create dependency loops in the DLL load order. This can result in a DLL being used before the system has executed its initialization code. Similarly, the entry-point function must not call the FreeLibrary function (or a function that calls FreeLibrary) during process
  • wine 无法加载我的自定义内置 dll 并报告“调用未实现的函数”(wine can't load my custom build-in dll and report "call to unimplemented function")
    问题 我无法登录 WINEHQ Bugzilla,所以我在这里问,这是要求: 我们有一个 Windows 应用程序,我们有 exe 源代码,它使用 ATL。 exe 依赖的 dll 需要一些特殊的设备,但大多数情况下它们都有 linux 版本。 现在我们需要将 windows 应用程序移植到 linux。 我最后的尝试:写一个中间dll来包装一些已经是跨平台的lib,exe会调用新的中间dll,我将中间dll命名为“WINE自定义内置dll”。 CUSTOM 意味着我编译这个 dll.so 由酿酒师独立。 一切都很好,直到在 wine64 下运行它,linux shell 中的命令行: wine64 portsome.exe 这是输出: wine: Call from 0x7bc5eeec to unimplemented function wrapsome.dll.wrap_SOME_GetVersion, aborting Backtrace: =>0 0x000000007bc5eeec stub_entry_point+0x5c(dll=<is not available>, name=<is not available>, ret_addr=<is not available>) [/home/root0/src/wine/build/dlls/ntdll/../../
  • cl.exe 在通过 MSBuild 调用时无限期挂起(cl.exe hangs indefinitely while being invoked via MSBuild)
    问题 我正在尝试在我的(主要是 C++)项目上运行 MSBuild(想象一下一个非常庞大的代码库)。 Visual Studio 2015 是有问题的工具集( Windows 7 SP1 和 VS 2015 Update 2 )。 即使使用 /m:1 (从而迫使它只使用一个处理器),我也发现一些完全随机的项目在编译阶段经常挂起。 例如,当这个问题发生时,如果我查看有问题的项目及其包含的文件,我可以看到已为每个翻译单元成功创建了 .obj 文件。 然而,系统永远不会进入链接阶段。 我看到两个 cl.exe 实例闲置在任务管理器上,什么也不做。 也许在 30 分钟左右后,当我杀死其中一个实例时,我会得到类似的信息: cl : Command line error D8040: error creating or communicating with child process [Path_To_The_Project_Where_The_Compiler_Was_Stuck.vcxproj] 在此之后,令人惊讶的是,编译器只是继续从它停止的地方开始。 有没有人遇到过类似的事情? 在过去的几周里,这让我很不爽! 更新了 IInspectable 查询的答案: 这看起来有点毛茸茸的。 拜托,有更好的编辑技巧的人,你能帮我以更好的方式格式化它,这样人们就不会瞪大眼睛吗? 0:002> ~
  • 从dll载入dll?(Loading a dll from a dll?)
    问题 从DLL加载DLL的最佳方法是什么? 我的问题是我无法在process_attach上加载dll,也无法从主程序加载dll,因为我无法控制主程序源。 因此,我也不能调用非dllmain函数。 回答1 在评论中进行了所有辩论之后,我认为最好以“真实”的答案概括我的立场。 首先,仍然不清楚为什么需要使用LoadLibrary在DllMain中加载dll。 这绝对是一个坏主意,因为你的DllMain被另一个LoadLibrary调用,其持有加载程序锁,由DllMain中的文档解释中运行: 在初始进程启动期间或在调用LoadLibrary之后,系统会扫描该进程的已加载DLL列表。 对于尚未使用DLL_PROCESS_ATTACH值调用的每个DLL,系统将调用DLL的入口点函数。 该调用是在导致进程地址空间发生变化的线程(例如,进程的主线程或称为LoadLibrary的线程)的上下文中进行的。 系统在整个过程中对入口点的访问进行序列化。 DllMain中的线程持有加载程序锁,因此无法动态加载或初始化其他DLL。 入口点功能应仅执行简单的初始化或终止任务。 它一定不能调用LoadLibrary或LoadLibraryEx函数(或调用这些函数的函数) ,因为这可能会以DLL加载顺序创建依赖关系循环。 这可能会导致在系统执行其初始化代码之前使用DLL。 同样
  • 挂钩线程的创建/终止(Hooking thread creation/termination)
    问题 是否可以在Windows上挂接到线程终止? IOW,如果进程内的一个线程(对其他进程及其线程不感兴趣)已终止(通常是-或更重要的是-强制终止),我想得到通知。 另外,也可以加入线程创建。 原理:我有一个基于每个线程管理某些信息的库(可以将其视为某些信息的整个进程的每个线程缓存)。 当线程终止时,我必须从缓存中删除所有特定于线程的信息。 [使用线程ID实现缓存关联,该ID可能会在以后的线程中重用。] “正常”执行顺序没有问题,因为库用户将从库中分离当前线程,这将清除状态。 如果有人杀死拥有缓存资源的线程,就会开始出现问题。 回答1 您可以使用Detours之类的工具来对Win32 API(例如TerminateThread)进行API级别的挂钩。 不过,我不明白您为什么需要这样做。 听起来好像您需要在线程死亡时清除该线程的关联缓存,以便在出现另一个具有相同ID的线程时可以重新使用该插槽。 这样对吗? 如果是这样,当您收到DLL_THREAD_ATTACH事件时,是否不能仅清除DllMain的缓存关联? 这实际上是您的新线程通知。 在这一点上,您知道您有一个新线程,那么清除现有的关联缓存不是安全的吗? 另一个可行的替代方法是线程本地存储(TLS)。 您可以使用Win32 API(例如TlsAlloc / TlsSetValue)来存储特定于线程的信息。 您还可以使用_
  • 如何在 DLLMain 中启动线程?(How to start a thread in DLLMain?)
    问题 如何在 DLLMain 中启动线程意味着 std::thread - 从根本上说。 No 表示 WinApi,STL 表示。 当我在流程中运行该函数时,我崩溃了从这个 DLL 调用的应用程序。 先感谢您。 此代码获取文件 (exe) 上的哈希和并将其写入文件。 (* 。文本)。 但是应用程序崩溃 void initialize() { string buffer; thread t(calclulateHash, ref(buffer)); t.detach(); } BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: { initialize(); break; } } return true; } 回答1 DllMain() 有一些限制。 您不应该在 DllMain 中进行任何阻塞调用,因为它是从 OS 加载程序调用的。 锁定加载程序可能会阻止某些线程启动,并且通常会导致不好的事情。 任何形式的锁定。 如果您试图获取当前由需要 OS 加载程序锁(您在从它执行时持有)的线程持有的锁,在最佳情况下您将死锁。 不允许启动线程,因为当你启动线程时.. 你通过操作系统加载器再次调用这个
  • 在DllMain中创建线程?(Creating a thread in DllMain?)
    问题 看来,当在DLL_PROCESS_ATTACH从DllMain中创建线程时,直到所有dll都已加载,该线程才开始。 由于我需要确保线程在继续运行之前能够运行,因此出现了死锁。 有什么方法可以强制线程启动? 回答1 您不应该从DLLMain进行任何API调用,尤其是对于诸如创建线程或窗口之类的事情。 雷蒙德·陈(Raymond Chen)已经写过很多次了; 这是一个特别相关的。 回答2 你的线程做什么? 如果您试图将内容移动到第二个线程上,以避免在DllMain内可以执行的操作受到限制,那么运气不好。 这些不是对DllMain可以做什么的限制,它们是对DllMain运行(并保持加载程序锁定)时可以做什么的限制。 如果您的线程需要获取加载程序锁,它将等待直到第一个线程完成使用它为止。 如果您的线程不需要加载程序锁,我不明白为什么它不能立即继续...但是没有诸如不需要加载程序锁的线程之类的东西。 Windows必须先将DLL_THREAD_ATTACH消息发送到所有DLL,然后线程才能开始运行,这意味着它还调用您自己的DllMain,并且Windows可以防止重新进入。 没有办法解决这个问题。 直到DLL_THREAD_ATTACH处理之后,线程才能启动,而当您的第一个线程位于DllMain中时,该线程就无法启动。 解决此问题的唯一可能方法是启动一个新进程
  • 可以调用.exe 的 DllMain 吗?(Can the DllMain of an .exe be called?)
    问题 我的问题与这个不完全相同(这不是理论上的,只有一个没有消息循环的主线程,InitInstance 和 ExitInstance 没有合适的调用)。 我正在使用没有消息循环的控制台应用程序; 此应用程序使用 LoadLibrary 函数加载一个 exe,以便它可以使用其导出的函数。 坏消息:没有调用exe的DllMain函数(我验证了符号表,使用def文件,DllMain出现正确); 文档说如果加载的模块是 DLL(太糟糕了),就会调用它。 当 LoadLibrary 被调用时(可能会再次在 FreeLibrary 被调用时),哪些条件(如果存在)会导致执行 exe 的 DllMain 函数? 最好的祝福 回答1 最明显的条件是调用 LoadLibrary() 的进程显式获取 GetProcAddress("DllMain") 然后调用它。 回答2 条件是: 1) 正在加载的二进制文件被编译为 DLL(使用 gcc/ld 时意味着使用--shared选项;如果使用--shared ,则生成的文件将是一个 dll,不会运行,见下文) 2) IMAGE_FILE_DLL设置在正在加载的二进制文件的 PE 文件头中。 如果它被设置,文件是一个 dll,当 Windows 链接器将这个文件链接到你的程序时,它会为你调用它的DllMain()函数(不管它是如何链接的 -
  • 如何从注入到同一程序的另一个.dll中调用函数?(How can I call a function from another .dll which is injected to the same program?)
    问题 我的问题确实在上面,但是我将在下面提供更多信息: 我有一个程序,该程序首先使用我的“ false” d3d9.dll,然后将该DLL加载到我进行反向工程的游戏中。 一段时间后,加载了.dll以及所有其他游戏依赖项,我想注入我的DLL,它将完成反向工程的所有肮脏工作。 我想我可以使用LoadLibrary将此DLL加载到程序中,但是当我使用DLL时,我注入它来运行主要的反向工程代码。 有没有可以用来从d3d9.dll调用某些函数的函数? 这是因为我仍然需要访问d3d9库,以将我可能想添加的内容与注入的.dll一起呈现在屏幕上。 我也不想只使用d3d9.dll,因为这将导致加载时间以及内存更改的问题。 我也不打算在DLL中使用DllMain,这意味着我还需要调用从d3d9.dll到注入的DLL的远程函数,以确保安全地启动进程。 抱歉,这是一个愚蠢的问题,但是感谢您的答复。 回答1 在过去,我们使用CreateRemoteThread并使用LoadLibraryA作为lpStartAddress的地址(该地址在所有进程中都相同)。 诀窍是使用VirtualAllocEx分配要注入的DLL名称,并将其用作lpParameter 。 实际上,您的线程使用您要注入的DLL名称调用LoadLibraryA。 加载Dll时,将调用Dllmain
  • C++中WinMain、main和DllMain的区别(Difference between WinMain,main and DllMain in C++)
    问题 这三个函数有什么区别,什么时候用?? 回答1 WinMain 用于应用程序(以 .exe 结尾)以指示进程正在启动。 它将为进程提供命令行参数,并作为进程的用户代码入口点。 WinMain(或不同版本的 main)也是一个必需的函数。 操作系统需要调用一个函数来启动进程运行。 DllMain 用于 DLL 以表示许多不同的场景。 最值得注意的是,它将在 DLL 加载到进程中:DLL_PROCESS_ATTACH DLL 从进程中卸载:DLL_PROCESS_DETACH 进程中启动了一个线程:DLL_THREAD_ATTACH 一个线程在进程中结束:DLL_THREAD_DETACH DllMain 是一个可选的构造并且有很多与之相关的隐式契约。 例如,您不应该调用会强制加载另一个 DLL 的代码。 一般来说,要正确使用它是相当困难的,除非您有非常特殊的需要,否则应该避免使用它。 回答2 main()表示你的程序是一个控制台应用程序。 WinMain()表示该程序是一个 GUI 应用程序——也就是说,它显示窗口和对话框而不是显示控制台。 DllMain()表示程序是一个 DLL。 DLL 不能直接运行,而是被上述两种应用程序使用。 所以: 当您编写要显示窗口等的程序时,请使用 WinMain。 编写 DLL 时使用 DLLMain。 在所有其他情况下使用 main。 回答3
  • LoadLibrary() 错误代码 127(LoadLibrary() error code 127)
    问题 我在使用 LoadLibrary() 时遇到问题,并收到一个对我来说没有意义的错误: ::SetLastError(0); m_hDll = ::LoadLibrary(szName); if (m_hDll == NULL) // Failure to load the DLL. { DWORD err = GetLastError(); } 错误是 127(“找不到指定的过程。”)这对我调用 LoadLibrary() 没有任何意义。 我还没有调用 GetProcaddress() 。 DLL(和应用程序)都是用 VS++ 2005 SP1 编译的。 可能出什么问题了? 回答1 让我们一步一步来: 该错误消息意味着找到了 dll,但缺少所需的功能。 (抖动是对的。)这意味着您拥有所需的 dll,但没有正确的版本。 (Davefiddes 是对的,尽管问题可能出在任何 dll 上,而不仅仅是 Microsoft 运行时库。而且,至少对于主要更新,Microsoft 为其运行时库提供了不同的名称,因此在这种情况下它不会成为问题。) 这没有意义,因为没有从正在加载的 dll 请求任何功能。 (亚当是对的。) 因此,预计丢失的函数不会在 LoadLibrary 命令显式加载的 dll 中找到,而是在同时隐式加载的依赖 dll 中找到,因为第一个 dll 需要它。