layout: post
title: 解決Windows Dll入口函數(shù)中Wait系函數(shù)死鎖的解決
categories: Windows
description: 解決Windows Dll入口函數(shù)中Wait系函數(shù)死鎖的解決
keywords:
url: https://lichao890427.github.io/ https://github.com/lichao890427/
案例
??dll中如果用了等待系函數(shù)會(huì)形成死鎖,《Windows核心編程》給出了一個(gè)例子
//***exe.cpp
LoadLibrary(***dll.cpp);
//***dll.cpp
DWORD __stdcall func(LPVOID)
{
Sleep(2000);
cout<<"in thread"<<endl;
return 0;
}
BOOL WINAPI DllMain(HINSTANCE hInstDll,DWORD fdwReason,PVOID fImpLoad)
{
if(fdwReason == DLL_PROCESS_ATTACH)
{
HANDLE hthread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func,NULL,0,NULL);
if(hthread)
WaitForSingleObject(hthread);//這里會(huì)死鎖
}
else if(fdwReason == DLL_THREAD_ATTACH)
{
}
}
分析
??這是一個(gè)很常見(jiàn)的錯(cuò)誤辜羊,死鎖原因是這樣的:在loadlibrary之后踏兜,系統(tǒng)加載器執(zhí)行到dllmain之前,會(huì)調(diào)用EnterCriticalSection鎖住進(jìn)程的“加載器鎖”八秃,而dllmain中的wait函數(shù)會(huì)共用這個(gè)“加載器鎖”碱妆,再次鎖住該鎖,因此造成死鎖昔驱,疹尾,,正常的情況是:waitForsingleobject之前就執(zhí)行完dllmain骤肛,之后系統(tǒng)加載器執(zhí)行LeaveCriticalSection纳本,之后再去wait就不會(huì)發(fā)生死鎖,而現(xiàn)在的情況是腋颠,wait在等dllmain執(zhí)行完繁成,而dllmain在等wait執(zhí)行完。dllmain中不能用wait系函數(shù)也是出自這個(gè)原因淑玫,如果讀了內(nèi)核代碼巾腕,會(huì)更明白這一塊面睛。核心編程的作者說(shuō),他能想到的解決辦法只有在dllmain中不要出現(xiàn)wait系函數(shù)祠墅,這種是不良設(shè)計(jì)思路侮穿。事實(shí)上MSDN上也說(shuō)不推薦dllmain中存在耗時(shí)操作,所以他們才這么設(shè)計(jì)的毁嗦!因?yàn)榧虞ddll本身就是很復(fù)雜的亲茅,很費(fèi)時(shí)間片的工作!9纷肌克锣!,核心編程的作者嘗試DisableThreadLibraryCall來(lái)破解限制,然而沒(méi)成功腔长。
解決
??這里我提出2種方式來(lái)破解鎖限制袭祟,且都可以成功,第一種比較暴力捞附,直接leave掉加載器鎖巾乳,wait之后再恢復(fù)即可,代碼如下:
if(reason == DLL_PROCESS_ATTACH)
{
cout<<"inside dll!"<<endl;
PCRITICAL_SECTION loaderlock=NULL;
_asm
{
mov eax,fs:[0x18];
mov eax,[eax+0x30];
mov eax,[eax+0xa0];
mov loaderlock,eax;
}
LeaveCriticalSection(loaderlock);
HANDLE hthread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func,NULL,0,NULL);
if(hthread)
{
cout<<"create succeed!"<<endl;
}
WaitForSingleObject(hthread,INFINITE);
EnterCriticalSection(loaderlock);
}
else if(reason == DLL_THREAD_ATTACH)
{
cout<<" DLL_THREAD_ATTACH"<<endl;
}
return 0;
??這種做法的缺點(diǎn)是鸟召,無(wú)法保證可重入性胆绊,但如果dll足夠簡(jiǎn)單的話,也不妨一試欧募,第二種則采用事件等待的方法压状,比較規(guī)矩:
void main()
{
HANDLE hEvent=CreateEventA(NULL,TRUE,FALSE,"Initial");
HMODULE hmod=LoadLibraryA("C:\\Users\\Administrator\\Documents\\Visual Studio 2010\\Projects\\test1\\Debug\\testdl.dll");
WaitForSingleObject(hEvent,INFINITE);
CloseHandle(hEvent);
cout<<"Success!!"<<endl;
}
DWORD __stdcall func(LPVOID)
{
cout<<"in thread"<<endl;
HANDLE hEvent=OpenEventA(EVENT_ALL_ACCESS,TRUE,"Initial");
if(hEvent)
{
SetEvent(hEvent);
CloseHandle(hEvent);
}
return 0;
}
int __stdcall DllMain(int,int reason,int)
{
if(reason == DLL_PROCESS_ATTACH)
{
cout<<"inside dll!"<<endl;
CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func,NULL,0,NULL);
}
else if(reason == DLL_THREAD_ATTACH)
{
cout<<" DLL_THREAD_ATTACH"<<endl;
}
return TRUE;
}
??為防止濫用,再次強(qiáng)調(diào)一下跟继,在dllmain中最好不要放置耗時(shí)操作种冬。