找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 4028|回复: 7

关于在Dll入口函数中Wait系函数死锁的解决

[复制链接]
发表于 2015-6-23 01:40:23 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
本帖最后由 元始天尊 于 2015-6-23 01:42 编辑

众所周知,dll中如果用了等待系函数会形成死锁,《Windows核心编程》给出了一个例子

  1. //***exe.cpp
  2. LoadLibrary(***dll.cpp);

  3. //***dll.cpp
  4. DWORD __stdcall func(LPVOID)
  5. {
  6.         Sleep(2000);
  7.         cout<<"in thread"<<endl;
  8.         return 0;
  9. }
  10. BOOL WINAPI DllMain(HINSTANCE hInstDll,DWORD fdwReason,PVOID fImpLoad)
  11. {
  12.         if(fdwReason == DLL_PROCESS_ATTACH)
  13.         {
  14.                 HANDLE hthread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func,NULL,0,NULL);
  15.                 if(hthread)
  16.                         WaitForSingleObject(hthread);//这里会死锁
  17.         }
  18.         else if(fdwReason == DLL_THREAD_ATTACH)
  19.         {

  20.         }
  21. }

复制代码


    这是一个很常见的错误,死锁原因是这样的:在loadlibrary之后,系统加载器执行到dllmain之前,会调用EnterCriticalSection锁住进程的“加载器锁”,而dllmain中的wait函数会共用这个“加载器锁”,再次锁住该锁,因此造成死锁,,,正常的情况是:waitForsingleobject之前就执行完dllmain,之后系统加载器执行LeaveCriticalSection,之后再去wait就不会发生死锁,而现在的情况是,wait在等dllmain执行完,而dllmain在等wait执行完。dllmain中不能用wait系函数也是出自这个原因,如果读了内核代码,会更明白这一块。核心编程的作者说,他能想到的解决办法只有在dllmain中不要出现wait系函数,这种是不良设计思路。事实上msdn上也说不推荐dllmain中存在耗时操作,所以他们才这么设计的!因为加载dll本身就是很复杂的,很费时间片的工作!!!,核心编程的作者尝试DisableThreadLibraryCall来破解限制,然而没成功
    而这里我提出2种方式来破解锁限制,且都可以成功,第一种比较暴力,直接leave掉加载器锁,wait之后再恢复即可,代码如下:

  1.         if(reason == DLL_PROCESS_ATTACH)
  2.         {
  3.                 cout<<"inside dll!"<<endl;
  4.                 PCRITICAL_SECTION loaderlock=NULL;
  5.                 _asm
  6.                 {
  7.                         mov eax,fs:[0x18];
  8.                         mov eax,[eax+0x30];
  9.                         mov eax,[eax+0xa0];
  10.                         mov loaderlock,eax;
  11.                 }
  12.                 LeaveCriticalSection(loaderlock);
  13.                 HANDLE hthread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func,NULL,0,NULL);
  14.                 if(hthread)
  15.                 {
  16.                         cout<<"create succeed!"<<endl;
  17.                 }
  18.                 WaitForSingleObject(hthread,INFINITE);
  19.                 EnterCriticalSection(loaderlock);
  20.         }
  21.         else if(reason == DLL_THREAD_ATTACH)
  22.         {
  23.                 cout<<" DLL_THREAD_ATTACH"<<endl;
  24.         }

  25.         return 0;
复制代码

这种做法的缺点是,无法保证可重入性,但如果dll足够简单的话,也不妨一试

第二种则采用事件等待的方法,比较规矩:

  1. ***exe.cpp
  2. void main()
  3. {
  4.         HANDLE hEvent=CreateEventA(NULL,TRUE,FALSE,"Initial");
  5.         HMODULE hmod=LoadLibraryA("C:\\Users\\Administrator\\Documents\\Visual Studio 2010\\Projects\\test1\\Debug\\testdl.dll");
  6.         WaitForSingleObject(hEvent,INFINITE);
  7.         CloseHandle(hEvent);
  8.         cout<<"Success!!"<<endl;
  9. }

  10. ***dll.cpp
  11. DWORD __stdcall func(LPVOID)
  12. {
  13.         cout<<"in thread"<<endl;
  14.         HANDLE hEvent=OpenEventA(EVENT_ALL_ACCESS,TRUE,"Initial");
  15.         if(hEvent)
  16.         {
  17.                 SetEvent(hEvent);
  18.                 CloseHandle(hEvent);
  19.         }
  20.         return 0;
  21. }

  22. int __stdcall DllMain(int,int reason,int)
  23. {
  24.         if(reason == DLL_PROCESS_ATTACH)
  25.         {
  26.                 cout<<"inside dll!"<<endl;
  27.                 CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)func,NULL,0,NULL);
  28.         }
  29.         else if(reason == DLL_THREAD_ATTACH)
  30.         {
  31.                 cout<<" DLL_THREAD_ATTACH"<<endl;
  32.         }
  33.         return TRUE;
  34. }

复制代码





为防止滥用,再次强调一下,在dllmain中最好不要放置耗时操作。


回复

使用道具 举报

发表于 2015-6-23 01:44:30 来自手机 | 显示全部楼层
哈哈 这个帖子好!
回复 赞! 靠!

使用道具 举报

发表于 2015-6-23 08:50:10 | 显示全部楼层
对这种代码必须表示不满:
        _asm
        {
                mov eax,fs:[0x18];
                mov eax,[eax+0x30];
                mov eax,[eax+0xa0];
                mov loaderlock,eax;
        }
X64、ARM、MIPS平台怎么办???
回复 赞! 靠!

使用道具 举报

发表于 2015-6-23 10:57:12 来自手机 | 显示全部楼层
除了从Fs段读这个不好办以外,别的几条可以纯C搞定。
回复 赞! 靠!

使用道具 举报

卡卡 该用户已被删除
发表于 2015-6-23 11:58:51 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2015-6-23 12:38:55 | 显示全部楼层
omgkaka 发表于 2015-6-23 11:58
第二种是调用方exe和被调用方dll协同处理这个问题
而一般实际需要dllmain中用这种操作的都是dll注入去处理 ...

该问题是源于“操作必须在dllmain中处理结束”而产生的,也就是LoadLibrary的时候一定要处理完
如果完全独立也可以,这种情况是创建了线程,而无需知道它是否执行完
然而如果一定要知道他执行完了的话,也就只能采用2种方式,要么等LoadLibrary执行完,这对应第一种方式,要么Load之前设置信号,load之后等待这个信号,这对应第二种方式,如果是规范的编程方式,那么其实这2种方法已经涵盖了所有方法。
回复 赞! 靠!

使用道具 举报

 楼主| 发表于 2015-6-23 20:06:43 | 显示全部楼层
0xAA55 发表于 2015-6-23 10:57
除了从Fs段读这个不好办以外,别的几条可以纯C搞定。

fs那个有NtGetPeb...等等函数,win提供了,我这篇只是揭示底层原理的
回复 赞! 靠!

使用道具 举报

发表于 2015-7-5 01:13:44 | 显示全部楼层
第二种搞法挺好,不过exe和dll得配合着来。
回复 赞! 靠!

使用道具 举报

本版积分规则

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-11-25 14:09 , Processed in 0.036203 second(s), 25 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表