您的位置:首页 > 大数据 > 人工智能

DllMain操作不当造成死锁

2016-05-20 16:42 423 查看
背景是这样的,做驻留模块的时候用到了dll劫持,需要在黑DLL(A)里面的dllmain里面调用驻留DLL(B)中的一些功能,但是在实际操作过程中却遇到了问题,现象是B中的功能没有被执行,连打印的DBG信息都没了(其实也就是死锁了),尝试过在dllmain中直接调用函数加载B,也尝试了在dllmain中创建线程加载B,都会出现一些问题,实在不想继续对dllmain云里雾里,于是决定去搞清楚。

首先整个流程是这样的:

exe加载A,A加载B。

1. 如果A中在Dll_process_attach中使用loadlibrary的方式加载B

A代码:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
 
WCHAR wzTarDll[MAX_PATH];
HANDLE hThread = 0;
 
VOID WINAPI ThreadLoadDll( void)
{
if ( LoadLibrary(wzTarDll))
{
MessageBoxW( NULL, L"thread load DllLockB done", L"DllLockA", MB_OK);
}
else
{
MessageBoxW( NULL, L"thread load DllLockB failed", L"DllLockA", MB_OK);
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD  ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
 
MessageBoxW( NULL, L"DLL_PROCESS_ATTACH", L"DllLockA", MB_OK);
//TODO
GetCurrentDirectoryW( MAX_PATH, wzTarDll);
 
wcscat( wzTarDll, L"\\DllLockB.dll");
 
if ( LoadLibrary(wzTarDll))
{
	MessageBoxW( NULL, L"load DllLockB.dll done", L"DllLockA", MB_OK);
}
else
{
	MessageBoxW( NULL, L"Load DllLockB.dll failed", L"DllLockA", MB_OK);
}
break;
 
case DLL_THREAD_ATTACH:
MessageBoxW( NULL, L"DLL_THREAD_ATTACH", L"DllLockA", MB_OK);
break;
 
case DLL_THREAD_DETACH:
MessageBoxW( NULL, L"DLL_THREAD_DETACH", L"DllLockA", MB_OK);
break;
 
case DLL_PROCESS_DETACH:
MessageBoxW( NULL, L"DLL_PROCESS_DETACH", L"DllLockA", MB_OK);
break;
}
return TRUE;
}


B代码:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
 
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD  ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
 
MessageBoxW( NULL, L"DLL_PROCESS_ATTACH", L"DllLockB", MB_OK);
break;
 
case DLL_THREAD_ATTACH:
 
MessageBoxW( NULL, L"DLL_THREAD_ATTACH", L"DllLockB", MB_OK);
 
break;
 
case DLL_THREAD_DETACH:
 
MessageBoxW( NULL, L"DLL_THREAD_DETACH", L"DllLockB", MB_OK);
 
break;
 
case DLL_PROCESS_DETACH:
 
MessageBoxW( NULL, L"DLL_PROCESS_DETACH", L"DllLockB", MB_OK);
 
break;
}
return TRUE;
}


EXE代码:

#include <Windows.h>
 
BOOL APIENTRY WinMain( __in HINSTANCE hInstance, __in_opt HINSTANCE hPrevInstance, __in_opt LPSTR lpCmdLine, __in int nShowCmd )
{
WCHAR wzTarDLL[MAX_PATH];
GetCurrentDirectoryW( MAX_PATH, wzTarDLL);
wcscat( wzTarDLL, L"\\DllLockA.dll");
if ( LoadLibraryW(wzTarDLL))
{
MessageBoxW( NULL, L"load DllLockA.dll done", L"DllLockEXE", MB_OK);
return TRUE;
}
return FALSE;
}


执行之后得出整个流程:
EXE开始加载——DLL_PROCESS_ATTACH(A中)——DLL_PROCESS_ATTACH(B中)——B加载完成(代码返回A中提示)——A加载完成(代码返回EXE)——DLL_PROCESS_DETACH(B中)——DLL_PROCESS_DETACH(A中)——结束。

用LoadLibrary加载时是以进程attach的方式加载,不会造成死锁。

2. A中DLL_PROCESS_ATTACH以创建线程的方式加载B

A代码:

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
 
WCHAR wzTarDll[MAX_PATH];
HANDLE hThread = 0;
 
VOID WINAPI ThreadLoadDll( void)
{
if ( LoadLibrary(wzTarDll))
{
MessageBoxW( NULL, L"thread load DllLockB done", L"DllLockA", MB_OK);
}
else
{
MessageBoxW( NULL, L"thread load DllLockB failed", L"DllLockA", MB_OK);
}
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD  ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
 
MessageBoxW( NULL, L"DLL_PROCESS_ATTACH", L"DllLockA", MB_OK);
//TODO
GetCurrentDirectoryW( MAX_PATH, wzTarDll);
 
wcscat( wzTarDll, L"\\DllLockB.dll");
hThread = CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)ThreadLoadDll, NULL, NULL, NULL);
WaitForSingleObject( hThread, INFINITE);
 
if (hThread)
{
	MessageBoxW( NULL, L"create thread done", L"DllLockA", MB_OK);
}
else
{
	WCHAR wzError[512];
	wsprintfW( wzError, L"create thread failed , error code  = %d", GetLastError());
	MessageBoxW( NULL, wzError, L"DllLockA", MB_OK);
}
 
break;
 
case DLL_THREAD_ATTACH:
MessageBoxW( NULL, L"DLL_THREAD_ATTACH", L"DllLockA", MB_OK);
break;
 
case DLL_THREAD_DETACH:
MessageBoxW( NULL, L"DLL_THREAD_DETACH", L"DllLockA", MB_OK);
break;
 
case DLL_PROCESS_DETACH:
MessageBoxW( NULL, L"DLL_PROCESS_DETACH", L"DllLockA", MB_OK);
break;
}
return TRUE;
}


B和EXE代码保持不变,此时弹出A的DLL_PROCESS_ATTACH消息框之后程序就没有反应了。程序死锁。死锁是由于CreateThread后面的WaitForSingleObject导致的,创建线程的方式加载另一个DLL时正常的顺序是:

EXE开始加载——DLL_PROCESS_ATTACH(A中)——创建线程完成(仅仅是完成创建,线程并没有返回)(A)——DLL_THREAD_ATTACH(A)——DLL_PROCESS_ATTACH(B)——A/B加载同时完成——DLL_THREAD_DETACH(B)——DLL_THREAD_DETACH(A)——DLL_PROCESS_DETACH(B)——DLL_PROCESS_DETACH(A)——结束

如果加了WaitForSingleObject,A会等待线程返回才能继续执行,但是此时需要先完成A.dll的DLL_THREAD_ATTACH才轮到B中DLL_PROCESS_ATTACH,线程在等待执行A的DLL_THREAD_ATTACH,A在等待线程的返回,因此造成死锁。

3. 总结

为了避免dllmain中产生死锁,首先要搞清楚case走的流程,其次应慎重使用等待类函数,诸如等待事件返回,等待线程返回的,避免互相等待。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c 线程