无法卸载注入的 DLL
Unable to unload injected DLL
我用 C 编写了一个 dll,我通过 CreateRemoteThread()
将其注入到 C 控制台程序中。
C 程序只是调用 Sleep(INFINITE)
,基本上充当注入的 dll 的宿主。
这是 DllMain:
HINSTANCE thisDllHandle = NULL;
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD entryReason, void *impLoad)
{
switch (entryReason)
{
case DLL_PROCESS_ATTACH:
{
thisDllHandle = hInstance;
HANDLE thread = (HANDLE)_beginthreadex(NULL, 0, Boss, NULL, 0, NULL);
if (thread) CloseHandle(thread);
}
}
return TRUE;
}
通过CreateRemoteThread()
然后returns创建的线程,而我的Boss线程是唯一的线程运行这个dll的代码。
如果我随后告诉 Boss 退出,清理后它会调用 FreeLibraryAndExitThread(thisDllHandle, 0);
- 线程退出但 dll 仍然加载在我的主机进程中。
使用 Brandon 在 here 上的回答中的想法,我让主机程序告诉我加载了哪些模块并显示模块的 GlblcntUsage
和 ProccntUsage
。
注入 dll 后,计数为 1。在 FreeLibraryAndExitThread()
后,计数为零 - 但 dll 仍在加载!为什么?
顺便说一句,如果我改为调用 FreeLibrary(thisDllHandle)
,主机程序会崩溃(正如预期的那样),但 dll 会被卸载。
编辑
总结一下我正在尝试做的事情:我试图通过创建一个加载我的 dll 的远程线程来在远程进程中注入一个 dll;该 dll 生成另一个线程,该线程运行相同的 dll 代码并一直存在,并且原始线程退出;然后我希望该线程卸载 dll 并退出。
在尝试减少代码以便 post 它以响应@David Heffernan 的评论时,我设法让它工作 - 通过在调用 FreeLibrary
之前调用退出线程 FreeLibrary
=20=]。但是我想了解为什么我必须两次释放它 - 我对这些东西不太了解。我认为这不需要代码,因为它非常简单:
- 获取远程进程的句柄。
- 在自己的 kernel32 模块中获取
LoadLibraryW
的地址。
- 在远程进程中分配内存,将[dll的路径]写入所述内存。
- 在远程进程句柄上调用
CreateRemoteThread
,传入第 2 步的地址作为起始地址,传入第 3 步的地址作为参数。
这会导致远程进程中的线程使用 LoadLibraryW
加载我的 dll。当我的 Dll 被加载时,它会启动一个新线程(参见前面的 DllMain 代码),它在我的 dll 中运行一些其他函数(这个其他函数中的内容并不重要 - 我可以让它休眠 10 秒然后调用 FreeLibraryAndExitThread
,行为相同)。我远程创建的原始线程死于 LoadLibraryW returns。所以现在只有一个线程 运行 我的 dll 代码。
当该线程调用 FreeLibraryAndExitThread
时,我希望我的 dll 从远程进程中卸载,因为该线程已释放库并退出。但 dll 仍处于加载状态,只有释放两次才能卸载它。
由于您在注入的 DllMain 函数中,并且由于您没有通过调用 _endthreadex() 函数退出线程,因此您可能应该使用 CreateThread() 而不是 _beginthreadex() 来启动新线程。避免使用 any C 运行时库函数也可能是明智的,尤其是当您无法确保您的 C 库与目标进程的库匹配时。
[我不清楚运行时库为何以及在什么情况下以所描述的方式运行,但 OP 报告说使用 CreateThread() 纠正了问题。我最好的猜测是它与 _onexit() 支持有关。]
我用 C 编写了一个 dll,我通过 CreateRemoteThread()
将其注入到 C 控制台程序中。
C 程序只是调用 Sleep(INFINITE)
,基本上充当注入的 dll 的宿主。
这是 DllMain:
HINSTANCE thisDllHandle = NULL;
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD entryReason, void *impLoad)
{
switch (entryReason)
{
case DLL_PROCESS_ATTACH:
{
thisDllHandle = hInstance;
HANDLE thread = (HANDLE)_beginthreadex(NULL, 0, Boss, NULL, 0, NULL);
if (thread) CloseHandle(thread);
}
}
return TRUE;
}
通过CreateRemoteThread()
然后returns创建的线程,而我的Boss线程是唯一的线程运行这个dll的代码。
如果我随后告诉 Boss 退出,清理后它会调用 FreeLibraryAndExitThread(thisDllHandle, 0);
- 线程退出但 dll 仍然加载在我的主机进程中。
使用 Brandon 在 here 上的回答中的想法,我让主机程序告诉我加载了哪些模块并显示模块的 GlblcntUsage
和 ProccntUsage
。
注入 dll 后,计数为 1。在 FreeLibraryAndExitThread()
后,计数为零 - 但 dll 仍在加载!为什么?
顺便说一句,如果我改为调用 FreeLibrary(thisDllHandle)
,主机程序会崩溃(正如预期的那样),但 dll 会被卸载。
编辑
总结一下我正在尝试做的事情:我试图通过创建一个加载我的 dll 的远程线程来在远程进程中注入一个 dll;该 dll 生成另一个线程,该线程运行相同的 dll 代码并一直存在,并且原始线程退出;然后我希望该线程卸载 dll 并退出。
在尝试减少代码以便 post 它以响应@David Heffernan 的评论时,我设法让它工作 - 通过在调用 FreeLibrary
之前调用退出线程 FreeLibrary
=20=]。但是我想了解为什么我必须两次释放它 - 我对这些东西不太了解。我认为这不需要代码,因为它非常简单:
- 获取远程进程的句柄。
- 在自己的 kernel32 模块中获取
LoadLibraryW
的地址。 - 在远程进程中分配内存,将[dll的路径]写入所述内存。
- 在远程进程句柄上调用
CreateRemoteThread
,传入第 2 步的地址作为起始地址,传入第 3 步的地址作为参数。
这会导致远程进程中的线程使用 LoadLibraryW
加载我的 dll。当我的 Dll 被加载时,它会启动一个新线程(参见前面的 DllMain 代码),它在我的 dll 中运行一些其他函数(这个其他函数中的内容并不重要 - 我可以让它休眠 10 秒然后调用 FreeLibraryAndExitThread
,行为相同)。我远程创建的原始线程死于 LoadLibraryW returns。所以现在只有一个线程 运行 我的 dll 代码。
当该线程调用 FreeLibraryAndExitThread
时,我希望我的 dll 从远程进程中卸载,因为该线程已释放库并退出。但 dll 仍处于加载状态,只有释放两次才能卸载它。
由于您在注入的 DllMain 函数中,并且由于您没有通过调用 _endthreadex() 函数退出线程,因此您可能应该使用 CreateThread() 而不是 _beginthreadex() 来启动新线程。避免使用 any C 运行时库函数也可能是明智的,尤其是当您无法确保您的 C 库与目标进程的库匹配时。
[我不清楚运行时库为何以及在什么情况下以所描述的方式运行,但 OP 报告说使用 CreateThread() 纠正了问题。我最好的猜测是它与 _onexit() 支持有关。]